From c5a6d8c94b826642286ab04ec3c1c8a8fbe9800b Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Tue, 22 Feb 2022 13:27:04 -0800 Subject: [PATCH 01/12] feat(connector): added initial apis for connector namespaces --- .../api/admin/private/api/openapi.yaml | 335 ++++++++- .../private/api_connector_clusters_admin.go | 108 ++- .../private/api_connector_namespaces_admin.go | 260 +++++++ .../internal/api/admin/private/client.go | 3 + .../private/model_connector_namespace.go | 30 + .../model_connector_namespace_eval_request.go | 16 + .../private/model_connector_namespace_list.go | 19 + .../private/model_connector_namespace_meta.go | 23 + .../model_connector_namespace_request.go | 17 + .../model_connector_namespace_request_meta.go | 16 + ...ctor_namespace_request_meta_annotations.go | 16 + .../model_connector_namespace_tenant.go | 17 + ...connector_namespace_tenant_organisation.go | 16 + .../model_connector_namespace_tenant_user.go | 16 + ...connector_namespace_with_tenant_request.go | 18 + .../connector/internal/api/dbapi/namespace.go | 41 ++ .../internal/api/public/api/openapi.yaml | 526 +++++++++++++ .../api/public/api_connector_namespaces.go | 693 ++++++++++++++++++ .../connector/internal/api/public/client.go | 3 + .../api/public/model_connector_namespace.go | 30 + .../model_connector_namespace_eval_request.go | 16 + .../public/model_connector_namespace_list.go | 19 + .../public/model_connector_namespace_meta.go | 23 + .../model_connector_namespace_request.go | 17 + .../model_connector_namespace_request_meta.go | 16 + ...ctor_namespace_request_meta_annotations.go | 16 + .../model_connector_namespace_target.go | 16 + .../model_connector_namespace_tenant.go | 17 + ...connector_namespace_tenant_organisation.go | 16 + .../model_connector_namespace_tenant_user.go | 16 + .../api/public/model_deployment_location.go | 5 +- .../connector/internal/config/connectors.go | 22 +- .../connector/internal/generated/bindata.go | 4 +- .../internal/handlers/connector_admin.go | 135 +++- .../internal/handlers/connector_namespace.go | 286 ++++++++ ...02070000_add_connector_namespace_tables.go | 91 +++ .../internal/migrations/migrations.go | 1 + .../presenters/connector_namespace.go | 158 ++++ .../internal/presenters/object_reference.go | 6 + .../connector/internal/routes/route_loader.go | 59 +- .../internal/services/namespace_service.go | 241 ++++++ internal/connector/providers.go | 4 +- .../test/integration/feature_test.go | 2 + .../features/connector-agent-api.feature | 4 +- .../features/connector-api.feature | 10 + .../connector-multitenancy-api.feature | 499 +++++++++++++ openapi/connector_mgmt-private-admin.yaml | 169 ++++- openapi/connector_mgmt.yaml | 449 +++++++++++- test/cucumber/cucumber.go | 15 +- test/cucumber/user.go | 15 + 50 files changed, 4477 insertions(+), 73 deletions(-) create mode 100644 internal/connector/internal/api/admin/private/api_connector_namespaces_admin.go create mode 100644 internal/connector/internal/api/admin/private/model_connector_namespace.go create mode 100644 internal/connector/internal/api/admin/private/model_connector_namespace_eval_request.go create mode 100644 internal/connector/internal/api/admin/private/model_connector_namespace_list.go create mode 100644 internal/connector/internal/api/admin/private/model_connector_namespace_meta.go create mode 100644 internal/connector/internal/api/admin/private/model_connector_namespace_request.go create mode 100644 internal/connector/internal/api/admin/private/model_connector_namespace_request_meta.go create mode 100644 internal/connector/internal/api/admin/private/model_connector_namespace_request_meta_annotations.go create mode 100644 internal/connector/internal/api/admin/private/model_connector_namespace_tenant.go create mode 100644 internal/connector/internal/api/admin/private/model_connector_namespace_tenant_organisation.go create mode 100644 internal/connector/internal/api/admin/private/model_connector_namespace_tenant_user.go create mode 100644 internal/connector/internal/api/admin/private/model_connector_namespace_with_tenant_request.go create mode 100644 internal/connector/internal/api/dbapi/namespace.go create mode 100644 internal/connector/internal/api/public/api_connector_namespaces.go create mode 100644 internal/connector/internal/api/public/model_connector_namespace.go create mode 100644 internal/connector/internal/api/public/model_connector_namespace_eval_request.go create mode 100644 internal/connector/internal/api/public/model_connector_namespace_list.go create mode 100644 internal/connector/internal/api/public/model_connector_namespace_meta.go create mode 100644 internal/connector/internal/api/public/model_connector_namespace_request.go create mode 100644 internal/connector/internal/api/public/model_connector_namespace_request_meta.go create mode 100644 internal/connector/internal/api/public/model_connector_namespace_request_meta_annotations.go create mode 100644 internal/connector/internal/api/public/model_connector_namespace_target.go create mode 100644 internal/connector/internal/api/public/model_connector_namespace_tenant.go create mode 100644 internal/connector/internal/api/public/model_connector_namespace_tenant_organisation.go create mode 100644 internal/connector/internal/api/public/model_connector_namespace_tenant_user.go create mode 100644 internal/connector/internal/handlers/connector_namespace.go create mode 100644 internal/connector/internal/migrations/202202070000_add_connector_namespace_tables.go create mode 100644 internal/connector/internal/presenters/connector_namespace.go create mode 100644 internal/connector/internal/services/namespace_service.go create mode 100644 internal/connector/test/integration/features/connector-multitenancy-api.feature diff --git a/internal/connector/internal/api/admin/private/api/openapi.yaml b/internal/connector/internal/api/admin/private/api/openapi.yaml index aa12b7e03..c7903df58 100644 --- a/internal/connector/internal/api/admin/private/api/openapi.yaml +++ b/internal/connector/internal/api/admin/private/api/openapi.yaml @@ -15,8 +15,9 @@ servers: url: / tags: - name: Connector Clusters Admin +- name: Connector Namespaces Admin paths: - /api/connector_mgmt/v1/admin/kafka_connector_clusters/: + /api/connector_mgmt/v1/admin/kafka_connector_clusters: get: operationId: listConnectorClusters parameters: @@ -416,6 +417,230 @@ paths: summary: upgrade a connector cluster tags: - Connector Clusters Admin + /api/connector_mgmt/v1/admin/kafka_connector_namespaces: + get: + operationId: getConnectorNamespaces + parameters: + - description: Page index + examples: + page: + value: "1" + in: query + name: page + required: false + schema: + type: string + - description: Number of items in each page + examples: + size: + value: "100" + in: query + name: size + required: false + schema: + type: string + - description: |- + Specifies the order by criteria. The syntax of this parameter is + similar to the syntax of the `order by` clause of an SQL statement. + Each query can be ordered by any of the `ConnectorType` fields. + For example, to return all Connector types ordered by their name, use the following syntax: + + ```sql + name asc + ``` + + To return all Connector types ordered by their name _and_ version, use the following syntax: + + ```sql + name asc, version asc + ``` + + If the parameter isn't provided, or if the value is empty, then + the results are ordered by name. + examples: + orderBy: + value: name asc + explode: true + in: query + name: orderBy + required: false + schema: + type: string + style: form + - description: | + Search criteria. + + The syntax of this parameter is similar to the syntax of the `where` clause of a + SQL statement. Allowed fields in the search are `name`, `description`, `version`, `label`, and `channel`. + Allowed operators are `<>`, `=`, or `LIKE`. + Allowed conjunctive operators are `AND` and `OR`. However, you can use a maximum of 10 conjunctions in a search query. + + Examples: + + To return a Connector Type with the name `aws-sqs-source` and the channel `stable`, use the following syntax: + + ``` + name = aws-sqs-source and channel = stable + ```[p-] + + To return a Kafka instance with a name that starts with `aws`, use the following syntax: + + ``` + name like aws%25 + ``` + + If the parameter isn't provided, or if the value is empty, then all the Connector Type + that the user has permission to see are returned. + + Note. If the query is invalid, an error is returned. + examples: + search: + value: name = aws-sqs-source and channel = stable + explode: true + in: query + name: search + required: false + schema: + type: string + style: form + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ConnectorNamespaceList' + description: Connector namespaces + "401": + content: + application/json: + examples: + "401Example": + $ref: '#/components/examples/401Example' + schema: + $ref: '#/components/schemas/Error' + description: Auth token is invalid + "404": + content: + application/json: + examples: + "404Example": + $ref: '#/components/examples/404Example' + schema: + $ref: '#/components/schemas/Error' + description: No matching connector namespace exists + "500": + content: + application/json: + examples: + "500Example": + $ref: '#/components/examples/500Example' + schema: + $ref: '#/components/schemas/Error' + description: Unexpected error occurred + security: + - Bearer: [] + summary: Get a list of available connector namespaces + tags: + - Connector Namespaces Admin + post: + operationId: createConnectorNamespace + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ConnectorNamespaceWithTenantRequest' + description: Namespace to create + required: true + responses: + "202": + content: + application/json: + schema: + $ref: '#/components/schemas/ConnectorNamespace' + description: Accepted + "401": + content: + application/json: + examples: + "401Example": + $ref: '#/components/examples/401Example' + schema: + $ref: '#/components/schemas/Error' + description: Auth token is invalid + "404": + content: + application/json: + examples: + "404Example": + $ref: '#/components/examples/404Example' + schema: + $ref: '#/components/schemas/Error' + description: No matching connector namespace exists + "500": + content: + application/json: + examples: + "500Example": + $ref: '#/components/examples/500Example' + schema: + $ref: '#/components/schemas/Error' + description: Unexpected error occurred + security: + - Bearer: [] + summary: Create a connector namespace + tags: + - Connector Namespaces Admin + /api/connector_mgmt/v1/admin/kafka_connector_namespaces/${namespace_id}: + delete: + operationId: deleteConnectorNamespace + parameters: + - description: The name of the namespace to delete + explode: false + in: path + name: namespace_id + required: true + schema: + type: string + style: simple + responses: + "204": + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + description: Deleted + "401": + content: + application/json: + examples: + "401Example": + $ref: '#/components/examples/401Example' + schema: + $ref: '#/components/schemas/Error' + description: Auth token is invalid + "404": + content: + application/json: + examples: + "404Example": + $ref: '#/components/examples/404Example' + schema: + $ref: '#/components/schemas/Error' + description: No matching connector cluster exists + "500": + content: + application/json: + examples: + "500Example": + $ref: '#/components/examples/500Example' + schema: + $ref: '#/components/schemas/Error' + description: Unexpected error occurred + security: + - Bearer: [] + summary: Delete a connector namespace + tags: + - Connector Clusters Admin components: examples: "401Example": @@ -488,6 +713,10 @@ components: operator: $ref: '#/components/schemas/ConnectorAvailableOperatorUpgrade_operator' type: object + ConnectorNamespaceWithTenantRequest: + allOf: + - $ref: '#/components/schemas/ConnectorNamespaceRequest' + - $ref: '#/components/schemas/ConnectorNamespaceWithTenantRequest_allOf' ConnectorClusterList: allOf: - $ref: '#/components/schemas/List' @@ -555,6 +784,66 @@ components: allOf: - $ref: '#/components/schemas/ObjectReference' - $ref: '#/components/schemas/Error_allOf' + ConnectorNamespaceList: + allOf: + - $ref: '#/components/schemas/List' + - $ref: '#/components/schemas/ConnectorNamespaceList_allOf' + ConnectorNamespace: + allOf: + - $ref: '#/components/schemas/ObjectReference' + - $ref: '#/components/schemas/ConnectorNamespaceMeta' + - $ref: '#/components/schemas/ConnectorNamespace_allOf' + description: A connector namespace + ConnectorNamespaceMeta: + allOf: + - $ref: '#/components/schemas/ObjectMeta' + - $ref: '#/components/schemas/ConnectorNamespaceRequestMeta' + ConnectorNamespaceRequestMeta: + properties: + name: + type: string + annotations: + items: + $ref: '#/components/schemas/ConnectorNamespaceRequestMeta_annotations' + type: array + required: + - name + type: object + ConnectorNamespaceTenant: + discriminator: + mapping: + user: '#/components/schemas/ConnectorNamespaceTenantUser' + organisation: '#/components/schemas/ConnectorNamespaceTenantOrganisation' + propertyName: kind + oneOf: + - $ref: '#/components/schemas/ConnectorNamespaceTenantUser' + - $ref: '#/components/schemas/ConnectorNamespaceTenantOrganisation' + required: + - kind + type: object + ConnectorNamespaceTenantUser: + properties: + kind: + type: string + user_id: + type: string + type: object + ConnectorNamespaceTenantOrganisation: + properties: + kind: + type: string + organisation_id: + type: string + type: object + ConnectorNamespaceRequest: + allOf: + - $ref: '#/components/schemas/ConnectorNamespaceEvalRequest' + - $ref: '#/components/schemas/ConnectorNamespaceRequest_allOf' + description: A connector namespace create request + ConnectorNamespaceEvalRequest: + allOf: + - $ref: '#/components/schemas/ConnectorNamespaceRequestMeta' + description: An evaluation connector namespace create request ConnectorAvailableTypeUpgradeList_allOf: properties: items: @@ -587,6 +876,10 @@ components: type: string available_id: type: string + ConnectorNamespaceWithTenantRequest_allOf: + properties: + tenant: + $ref: '#/components/schemas/ConnectorNamespaceTenant' ConnectorClusterList_allOf: properties: items: @@ -607,6 +900,46 @@ components: type: string operation_id: type: string + ConnectorNamespaceList_allOf: + properties: + items: + items: + $ref: '#/components/schemas/ConnectorNamespace' + type: array + ConnectorNamespace_allOf: + properties: + name: + type: string + version: + format: int64 + type: integer + cluster_id: + type: string + expiration: + type: string + tenant: + $ref: '#/components/schemas/ConnectorNamespaceTenant' + required: + - cluster_id + - id + - name + - tenant + - version + ConnectorNamespaceRequestMeta_annotations: + properties: + name: + type: string + value: + type: string + required: + - name + - value + ConnectorNamespaceRequest_allOf: + properties: + cluster_id: + type: string + required: + - name securitySchemes: Bearer: bearerFormat: JWT diff --git a/internal/connector/internal/api/admin/private/api_connector_clusters_admin.go b/internal/connector/internal/api/admin/private/api_connector_clusters_admin.go index 80fd8286a..01007ac72 100644 --- a/internal/connector/internal/api/admin/private/api_connector_clusters_admin.go +++ b/internal/connector/internal/api/admin/private/api_connector_clusters_admin.go @@ -26,6 +26,112 @@ var ( // ConnectorClustersAdminApiService ConnectorClustersAdminApi service type ConnectorClustersAdminApiService service +/* +DeleteConnectorNamespace Delete a connector namespace + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param namespaceId The name of the namespace to delete +@return Error +*/ +func (a *ConnectorClustersAdminApiService) DeleteConnectorNamespace(ctx _context.Context, namespaceId string) (Error, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodDelete + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue Error + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/api/connector_mgmt/v1/admin/kafka_connector_namespaces/${namespace_id}" + localVarPath = strings.Replace(localVarPath, "{"+"namespace_id"+"}", _neturl.QueryEscape(parameterToString(namespaceId, "")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(r) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + // GetConnectorUpgradesByOperatorOpts Optional parameters for the method 'GetConnectorUpgradesByOperator' type GetConnectorUpgradesByOperatorOpts struct { Page optional.String @@ -297,7 +403,7 @@ func (a *ConnectorClustersAdminApiService) ListConnectorClusters(ctx _context.Co ) // create path and map variables - localVarPath := a.client.cfg.BasePath + "/api/connector_mgmt/v1/admin/kafka_connector_clusters/" + localVarPath := a.client.cfg.BasePath + "/api/connector_mgmt/v1/admin/kafka_connector_clusters" localVarHeaderParams := make(map[string]string) localVarQueryParams := _neturl.Values{} localVarFormParams := _neturl.Values{} diff --git a/internal/connector/internal/api/admin/private/api_connector_namespaces_admin.go b/internal/connector/internal/api/admin/private/api_connector_namespaces_admin.go new file mode 100644 index 000000000..bbfac794c --- /dev/null +++ b/internal/connector/internal/api/admin/private/api_connector_namespaces_admin.go @@ -0,0 +1,260 @@ +/* + * Connector Service Fleet Manager Admin APIs + * + * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. + * + * API version: 0.0.3 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package private + +import ( + _context "context" + "github.com/antihax/optional" + _ioutil "io/ioutil" + _nethttp "net/http" + _neturl "net/url" +) + +// Linger please +var ( + _ _context.Context +) + +// ConnectorNamespacesAdminApiService ConnectorNamespacesAdminApi service +type ConnectorNamespacesAdminApiService service + +/* +CreateConnectorNamespace Create a connector namespace + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param connectorNamespaceWithTenantRequest Namespace to create +@return ConnectorNamespace +*/ +func (a *ConnectorNamespacesAdminApiService) CreateConnectorNamespace(ctx _context.Context, connectorNamespaceWithTenantRequest ConnectorNamespaceWithTenantRequest) (ConnectorNamespace, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodPost + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ConnectorNamespace + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/api/connector_mgmt/v1/admin/kafka_connector_namespaces" + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = &connectorNamespaceWithTenantRequest + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(r) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +// GetConnectorNamespacesOpts Optional parameters for the method 'GetConnectorNamespaces' +type GetConnectorNamespacesOpts struct { + Page optional.String + Size optional.String + OrderBy optional.String + Search optional.String +} + +/* +GetConnectorNamespaces Get a list of available connector namespaces + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param optional nil or *GetConnectorNamespacesOpts - Optional Parameters: + * @param "Page" (optional.String) - Page index + * @param "Size" (optional.String) - Number of items in each page + * @param "OrderBy" (optional.String) - Specifies the order by criteria. The syntax of this parameter is similar to the syntax of the `order by` clause of an SQL statement. Each query can be ordered by any of the `ConnectorType` fields. For example, to return all Connector types ordered by their name, use the following syntax: ```sql name asc ``` To return all Connector types ordered by their name _and_ version, use the following syntax: ```sql name asc, version asc ``` If the parameter isn't provided, or if the value is empty, then the results are ordered by name. + * @param "Search" (optional.String) - Search criteria. The syntax of this parameter is similar to the syntax of the `where` clause of a SQL statement. Allowed fields in the search are `name`, `description`, `version`, `label`, and `channel`. Allowed operators are `<>`, `=`, or `LIKE`. Allowed conjunctive operators are `AND` and `OR`. However, you can use a maximum of 10 conjunctions in a search query. Examples: To return a Connector Type with the name `aws-sqs-source` and the channel `stable`, use the following syntax: ``` name = aws-sqs-source and channel = stable ```[p-] To return a Kafka instance with a name that starts with `aws`, use the following syntax: ``` name like aws%25 ``` If the parameter isn't provided, or if the value is empty, then all the Connector Type that the user has permission to see are returned. Note. If the query is invalid, an error is returned. +@return ConnectorNamespaceList +*/ +func (a *ConnectorNamespacesAdminApiService) GetConnectorNamespaces(ctx _context.Context, localVarOptionals *GetConnectorNamespacesOpts) (ConnectorNamespaceList, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodGet + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ConnectorNamespaceList + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/api/connector_mgmt/v1/admin/kafka_connector_namespaces" + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + if localVarOptionals != nil && localVarOptionals.Page.IsSet() { + localVarQueryParams.Add("page", parameterToString(localVarOptionals.Page.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.Size.IsSet() { + localVarQueryParams.Add("size", parameterToString(localVarOptionals.Size.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.OrderBy.IsSet() { + localVarQueryParams.Add("orderBy", parameterToString(localVarOptionals.OrderBy.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.Search.IsSet() { + localVarQueryParams.Add("search", parameterToString(localVarOptionals.Search.Value(), "")) + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(r) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} diff --git a/internal/connector/internal/api/admin/private/client.go b/internal/connector/internal/api/admin/private/client.go index 1a51679cd..9a3b61f73 100644 --- a/internal/connector/internal/api/admin/private/client.go +++ b/internal/connector/internal/api/admin/private/client.go @@ -49,6 +49,8 @@ type APIClient struct { // API Services ConnectorClustersAdminApi *ConnectorClustersAdminApiService + + ConnectorNamespacesAdminApi *ConnectorNamespacesAdminApiService } type service struct { @@ -68,6 +70,7 @@ func NewAPIClient(cfg *Configuration) *APIClient { // API Services c.ConnectorClustersAdminApi = (*ConnectorClustersAdminApiService)(&c.common) + c.ConnectorNamespacesAdminApi = (*ConnectorNamespacesAdminApiService)(&c.common) return c } diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace.go b/internal/connector/internal/api/admin/private/model_connector_namespace.go new file mode 100644 index 000000000..f56a75b08 --- /dev/null +++ b/internal/connector/internal/api/admin/private/model_connector_namespace.go @@ -0,0 +1,30 @@ +/* + * Connector Service Fleet Manager Admin APIs + * + * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. + * + * API version: 0.0.3 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package private + +import ( + "time" +) + +// ConnectorNamespace A connector namespace +type ConnectorNamespace struct { + Id string `json:"id"` + Kind string `json:"kind,omitempty"` + Href string `json:"href,omitempty"` + Owner string `json:"owner,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty"` + ModifiedAt time.Time `json:"modified_at,omitempty"` + Name string `json:"name"` + Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` + Version int64 `json:"version"` + ClusterId string `json:"cluster_id"` + Expiration string `json:"expiration,omitempty"` + Tenant ConnectorNamespaceTenant `json:"tenant"` +} diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_eval_request.go b/internal/connector/internal/api/admin/private/model_connector_namespace_eval_request.go new file mode 100644 index 000000000..02b27f994 --- /dev/null +++ b/internal/connector/internal/api/admin/private/model_connector_namespace_eval_request.go @@ -0,0 +1,16 @@ +/* + * Connector Service Fleet Manager Admin APIs + * + * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. + * + * API version: 0.0.3 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package private + +// ConnectorNamespaceEvalRequest An evaluation connector namespace create request +type ConnectorNamespaceEvalRequest struct { + Name string `json:"name"` + Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` +} diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_list.go b/internal/connector/internal/api/admin/private/model_connector_namespace_list.go new file mode 100644 index 000000000..50f56c10f --- /dev/null +++ b/internal/connector/internal/api/admin/private/model_connector_namespace_list.go @@ -0,0 +1,19 @@ +/* + * Connector Service Fleet Manager Admin APIs + * + * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. + * + * API version: 0.0.3 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package private + +// ConnectorNamespaceList struct for ConnectorNamespaceList +type ConnectorNamespaceList struct { + Kind string `json:"kind"` + Page int32 `json:"page"` + Size int32 `json:"size"` + Total int32 `json:"total"` + Items []ConnectorNamespace `json:"items"` +} diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_meta.go b/internal/connector/internal/api/admin/private/model_connector_namespace_meta.go new file mode 100644 index 000000000..40b4deec2 --- /dev/null +++ b/internal/connector/internal/api/admin/private/model_connector_namespace_meta.go @@ -0,0 +1,23 @@ +/* + * Connector Service Fleet Manager Admin APIs + * + * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. + * + * API version: 0.0.3 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package private + +import ( + "time" +) + +// ConnectorNamespaceMeta struct for ConnectorNamespaceMeta +type ConnectorNamespaceMeta struct { + Owner string `json:"owner,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty"` + ModifiedAt time.Time `json:"modified_at,omitempty"` + Name string `json:"name"` + Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` +} diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_request.go b/internal/connector/internal/api/admin/private/model_connector_namespace_request.go new file mode 100644 index 000000000..27357c0a7 --- /dev/null +++ b/internal/connector/internal/api/admin/private/model_connector_namespace_request.go @@ -0,0 +1,17 @@ +/* + * Connector Service Fleet Manager Admin APIs + * + * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. + * + * API version: 0.0.3 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package private + +// ConnectorNamespaceRequest A connector namespace create request +type ConnectorNamespaceRequest struct { + Name string `json:"name"` + Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` + ClusterId string `json:"cluster_id,omitempty"` +} diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_request_meta.go b/internal/connector/internal/api/admin/private/model_connector_namespace_request_meta.go new file mode 100644 index 000000000..35bd142db --- /dev/null +++ b/internal/connector/internal/api/admin/private/model_connector_namespace_request_meta.go @@ -0,0 +1,16 @@ +/* + * Connector Service Fleet Manager Admin APIs + * + * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. + * + * API version: 0.0.3 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package private + +// ConnectorNamespaceRequestMeta struct for ConnectorNamespaceRequestMeta +type ConnectorNamespaceRequestMeta struct { + Name string `json:"name"` + Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` +} diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_request_meta_annotations.go b/internal/connector/internal/api/admin/private/model_connector_namespace_request_meta_annotations.go new file mode 100644 index 000000000..087dab5fa --- /dev/null +++ b/internal/connector/internal/api/admin/private/model_connector_namespace_request_meta_annotations.go @@ -0,0 +1,16 @@ +/* + * Connector Service Fleet Manager Admin APIs + * + * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. + * + * API version: 0.0.3 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package private + +// ConnectorNamespaceRequestMetaAnnotations struct for ConnectorNamespaceRequestMetaAnnotations +type ConnectorNamespaceRequestMetaAnnotations struct { + Name string `json:"name"` + Value string `json:"value"` +} diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_tenant.go b/internal/connector/internal/api/admin/private/model_connector_namespace_tenant.go new file mode 100644 index 000000000..d8e4e78d2 --- /dev/null +++ b/internal/connector/internal/api/admin/private/model_connector_namespace_tenant.go @@ -0,0 +1,17 @@ +/* + * Connector Service Fleet Manager Admin APIs + * + * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. + * + * API version: 0.0.3 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package private + +// ConnectorNamespaceTenant struct for ConnectorNamespaceTenant +type ConnectorNamespaceTenant struct { + Kind string `json:"kind"` + UserId string `json:"user_id,omitempty"` + OrganisationId string `json:"organisation_id,omitempty"` +} diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_tenant_organisation.go b/internal/connector/internal/api/admin/private/model_connector_namespace_tenant_organisation.go new file mode 100644 index 000000000..a1b8a6948 --- /dev/null +++ b/internal/connector/internal/api/admin/private/model_connector_namespace_tenant_organisation.go @@ -0,0 +1,16 @@ +/* + * Connector Service Fleet Manager Admin APIs + * + * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. + * + * API version: 0.0.3 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package private + +// ConnectorNamespaceTenantOrganisation struct for ConnectorNamespaceTenantOrganisation +type ConnectorNamespaceTenantOrganisation struct { + Kind string `json:"kind,omitempty"` + OrganisationId string `json:"organisation_id,omitempty"` +} diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_tenant_user.go b/internal/connector/internal/api/admin/private/model_connector_namespace_tenant_user.go new file mode 100644 index 000000000..fd66f44cf --- /dev/null +++ b/internal/connector/internal/api/admin/private/model_connector_namespace_tenant_user.go @@ -0,0 +1,16 @@ +/* + * Connector Service Fleet Manager Admin APIs + * + * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. + * + * API version: 0.0.3 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package private + +// ConnectorNamespaceTenantUser struct for ConnectorNamespaceTenantUser +type ConnectorNamespaceTenantUser struct { + Kind string `json:"kind,omitempty"` + UserId string `json:"user_id,omitempty"` +} diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_with_tenant_request.go b/internal/connector/internal/api/admin/private/model_connector_namespace_with_tenant_request.go new file mode 100644 index 000000000..a2ec3830d --- /dev/null +++ b/internal/connector/internal/api/admin/private/model_connector_namespace_with_tenant_request.go @@ -0,0 +1,18 @@ +/* + * Connector Service Fleet Manager Admin APIs + * + * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. + * + * API version: 0.0.3 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package private + +// ConnectorNamespaceWithTenantRequest struct for ConnectorNamespaceWithTenantRequest +type ConnectorNamespaceWithTenantRequest struct { + Name string `json:"name"` + Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` + ClusterId string `json:"cluster_id,omitempty"` + Tenant ConnectorNamespaceTenant `json:"tenant,omitempty"` +} diff --git a/internal/connector/internal/api/dbapi/namespace.go b/internal/connector/internal/api/dbapi/namespace.go new file mode 100644 index 000000000..02e98b41d --- /dev/null +++ b/internal/connector/internal/api/dbapi/namespace.go @@ -0,0 +1,41 @@ +package dbapi + +import ( + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/db" + "time" +) + +type ConnectorTenantUser struct { + db.Model // user id in Id, required for references and data consistency +} + +type ConnectorTenantOrganisation struct { + db.Model // org id in Id, required for references and data consistency +} + +type ConnectorNamespaceAnnotation struct { + NamespaceId string `gorm:"primaryKey;index"` + Name string `gorm:"primaryKey;not null"` + Value string `gorm:"not null"` +} + +type ConnectorNamespace struct { + db.Model + Name string `gorm:"not null;uniqueIndex:idx_connector_namespaces_name_cluster_id"` + ClusterId string `gorm:"not null;uniqueIndex:idx_connector_namespaces_name_cluster_id;index"` + + Owner string `gorm:"not null;index"` + Version int64 `gorm:"type:bigserial;index"` + Expiration *time.Time + + // metadata + Annotations []ConnectorNamespaceAnnotation `gorm:"foreignKey:NamespaceId;references:ID"` + + // tenant, only one of the below fields can be not null + TenantUserId *string `gorm:"index:connector_namespaces_user_organisation_idx;index:,where:tenant_user_id is not null"` + TenantOrganisationId *string `gorm:"index:connector_namespaces_user_organisation_idx;index:,where:tenant_organisation_id is not null"` + TenantUser *ConnectorTenantUser `gorm:"foreignKey:TenantUserId"` + TenantOrganisation *ConnectorTenantOrganisation `gorm:"foreignKey:TenantOrganisationId"` +} + +type ConnectorNamespaceList []*ConnectorNamespace diff --git a/internal/connector/internal/api/public/api/openapi.yaml b/internal/connector/internal/api/public/api/openapi.yaml index 8e8f8137e..e5cc0dde6 100644 --- a/internal/connector/internal/api/public/api/openapi.yaml +++ b/internal/connector/internal/api/public/api/openapi.yaml @@ -840,6 +840,407 @@ paths: summary: Get a connector cluster's addon parameters tags: - Connector Clusters + /api/connector_mgmt/v1/kafka_connector_namespaces: + get: + description: Returns a list of connector namespaces + operationId: listConnectorNamespaces + parameters: + - description: Page index + examples: + page: + value: "1" + explode: true + in: query + name: page + required: false + schema: + type: string + style: form + - description: Number of items in each page + examples: + size: + value: "100" + explode: true + in: query + name: size + required: false + schema: + type: string + style: form + - description: |- + Specifies the order by criteria. The syntax of this parameter is + similar to the syntax of the `order by` clause of an SQL statement. + Each query can be ordered by any of the `ConnectorType` fields. + For example, to return all Connector types ordered by their name, use the following syntax: + + ```sql + name asc + ``` + + To return all Connector types ordered by their name _and_ version, use the following syntax: + + ```sql + name asc, version asc + ``` + + If the parameter isn't provided, or if the value is empty, then + the results are ordered by name. + examples: + orderBy: + value: name asc + explode: true + in: query + name: orderBy + required: false + schema: + type: string + style: form + - description: | + Search criteria. + + The syntax of this parameter is similar to the syntax of the `where` clause of a + SQL statement. Allowed fields in the search are `name`, `description`, `version`, `label`, and `channel`. + Allowed operators are `<>`, `=`, or `LIKE`. + Allowed conjunctive operators are `AND` and `OR`. However, you can use a maximum of 10 conjunctions in a search query. + + Examples: + + To return a Connector Type with the name `aws-sqs-source` and the channel `stable`, use the following syntax: + + ``` + name = aws-sqs-source and channel = stable + ```[p-] + + To return a Kafka instance with a name that starts with `aws`, use the following syntax: + + ``` + name like aws%25 + ``` + + If the parameter isn't provided, or if the value is empty, then all the Connector Type + that the user has permission to see are returned. + + Note. If the query is invalid, an error is returned. + examples: + search: + value: name = aws-sqs-source and channel = stable + explode: true + in: query + name: search + required: false + schema: + type: string + style: form + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ConnectorNamespaceList' + description: A list of connector namespaces + "401": + content: + application/json: + examples: + "401Example": + $ref: '#/components/examples/401Example' + schema: + $ref: '#/components/schemas/Error' + description: Auth token is invalid + "500": + content: + application/json: + examples: + "500Example": + $ref: '#/components/examples/500Example' + schema: + $ref: '#/components/schemas/Error' + description: Unexpected error occurred + security: + - Bearer: [] + summary: Returns a list of connector namespaces + tags: + - Connector Namespaces + post: + description: Create a new connector namespace + operationId: createConnectorNamespace + requestBody: + content: + application/json: + examples: + ConnectorNamespaceCreateExample: + $ref: '#/components/examples/ConnectorNamespaceCreateExample' + schema: + $ref: '#/components/schemas/ConnectorNamespaceRequest' + description: Connector namespace data + required: true + responses: + "202": + content: + application/json: + schema: + $ref: '#/components/schemas/ConnectorNamespace' + description: Accepted + "400": + content: + application/json: + examples: + "400CreationExample": + $ref: '#/components/examples/400CreationExample' + schema: + $ref: '#/components/schemas/Error' + description: Validation errors occurred + "401": + content: + application/json: + examples: + "401Example": + $ref: '#/components/examples/401Example' + schema: + $ref: '#/components/schemas/Error' + description: Auth token is invalid + "404": + content: + application/json: + examples: + "404Example": + $ref: '#/components/examples/404Example' + schema: + $ref: '#/components/schemas/Error' + description: The requested resource doesn't exist + "500": + content: + application/json: + examples: + "500Example": + $ref: '#/components/examples/500Example' + schema: + $ref: '#/components/schemas/Error' + description: An unexpected error occurred creating the connector namespace + security: + - Bearer: [] + summary: Create a new connector namespace + tags: + - Connector Namespaces + /api/connector_mgmt/v1/kafka_connector_namespaces/{connector_namespace_id}: + delete: + description: Delete a connector namespace + operationId: deleteConnectorNamespace + parameters: + - description: The id of the connector namespace + explode: false + in: path + name: connector_namespace_id + required: true + schema: + type: string + style: simple + responses: + "204": + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + description: Deleted + "401": + content: + application/json: + examples: + "401Example": + $ref: '#/components/examples/401Example' + schema: + $ref: '#/components/schemas/Error' + description: Auth token is invalid + "404": + content: + application/json: + examples: + "404DeleteExample": + $ref: '#/components/examples/404DeleteExample' + schema: + $ref: '#/components/schemas/Error' + description: No resource with specified ID exists + "500": + content: + application/json: + examples: + "500DeleteExample": + $ref: '#/components/examples/500DeleteExample' + schema: + $ref: '#/components/schemas/Error' + description: Unexpected error occurred + security: + - Bearer: [] + summary: Delete a connector namespace + tags: + - Connector Namespaces + get: + description: Get a connector namespace + operationId: getConnectorNamespace + parameters: + - description: The id of the connector namespace + explode: false + in: path + name: connector_namespace_id + required: true + schema: + type: string + style: simple + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ConnectorNamespace' + description: The connector namespace matching the request + "401": + content: + application/json: + examples: + "401Example": + $ref: '#/components/examples/401Example' + schema: + $ref: '#/components/schemas/Error' + description: Auth token is invalid + "404": + content: + application/json: + examples: + "404Example": + $ref: '#/components/examples/404Example' + schema: + $ref: '#/components/schemas/Error' + description: No matching connector namespace type exists + "500": + content: + application/json: + examples: + "500Example": + $ref: '#/components/examples/500Example' + schema: + $ref: '#/components/schemas/Error' + description: Unexpected error occurred + security: + - Bearer: [] + summary: Get a connector namespace + tags: + - Connector Namespaces + put: + description: udpate a connector namespace + operationId: updateConnectorNamespaceById + parameters: + - description: The id of the connector namespace + explode: false + in: path + name: connector_namespace_id + required: true + schema: + type: string + style: simple + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ConnectorNamespaceRequest' + description: Data to updated connector with + required: true + responses: + "204": + description: Namespace status is updated + "401": + content: + application/json: + examples: + "401Example": + $ref: '#/components/examples/401Example' + schema: + $ref: '#/components/schemas/Error' + description: Auth token is invalid + "404": + content: + application/json: + examples: + "404Example": + $ref: '#/components/examples/404Example' + schema: + $ref: '#/components/schemas/Error' + description: No matching connector namespace exists + "500": + content: + application/json: + examples: + "500Example": + $ref: '#/components/examples/500Example' + schema: + $ref: '#/components/schemas/Error' + description: Unexpected error occurred + security: + - Bearer: [] + summary: udpate a connector namespace + tags: + - Connector Namespaces + /api/connector_mgmt/v1/kafka_connector_namespaces/eval: + post: + description: Create a new evaluation connector namespace + operationId: createEvaluationNamespace + requestBody: + content: + application/json: + examples: + ConnectorNamespaceEvalCreateExample: + $ref: '#/components/examples/ConnectorNamespaceEvalCreateExample' + schema: + $ref: '#/components/schemas/ConnectorNamespaceEvalRequest' + description: Connector namespace data + required: true + responses: + "202": + content: + application/json: + schema: + $ref: '#/components/schemas/ConnectorNamespace' + description: Accepted + "400": + content: + application/json: + examples: + "400CreationExample": + $ref: '#/components/examples/400CreationExample' + schema: + $ref: '#/components/schemas/Error' + description: Validation errors occurred + "401": + content: + application/json: + examples: + "401Example": + $ref: '#/components/examples/401Example' + schema: + $ref: '#/components/schemas/Error' + description: Auth token is invalid + "404": + content: + application/json: + examples: + "404Example": + $ref: '#/components/examples/404Example' + schema: + $ref: '#/components/schemas/Error' + description: The requested resource doesn't exist + "500": + content: + application/json: + examples: + "500Example": + $ref: '#/components/examples/500Example' + schema: + $ref: '#/components/schemas/Error' + description: An unexpected error occurred creating the connector namespace + security: + - Bearer: [] + summary: Create a new short lived evaluation connector namespace + tags: + - Connector Namespaces components: examples: ConnectorClusterCreateExample: @@ -977,6 +1378,19 @@ components: error_handling: dead_letter_queue: topic: dlq + ConnectorNamespaceCreateExample: + value: + name: MyNamespace + cluster_id: 9bsv0s7tne7g02gh5g4g + annotations: + - name: connector_mgmt.api.openshift.com/profile + value: default-profile + ConnectorNamespaceEvalCreateExample: + value: + name: MyEvalNamespace + annotations: + - name: connector_mgmt.api.openshift.com/profile + value: default-profile "400CreationExample": value: id: "103" @@ -1211,9 +1625,11 @@ components: discriminator: mapping: ConnectorCluster: '#/components/schemas/ConnectorClusterTarget' + ConnectorNamespace: '#/components/schemas/ConnectorNamespaceTarget' propertyName: kind oneOf: - $ref: '#/components/schemas/ConnectorClusterTarget' + - $ref: '#/components/schemas/ConnectorNamespaceTarget' ConnectorClusterTarget: description: Targets workloads to an addon cluster properties: @@ -1224,6 +1640,16 @@ components: required: - kind type: object + ConnectorNamespaceTarget: + description: Targets workloads to an addon cluster namespace + properties: + kind: + type: string + namespace_id: + type: string + required: + - kind + type: object VersionMetadata: allOf: - $ref: '#/components/schemas/ObjectReference' @@ -1378,6 +1804,66 @@ components: allOf: - $ref: '#/components/schemas/List' - $ref: '#/components/schemas/ConnectorTypeList_allOf' + ConnectorNamespaceRequestMeta: + properties: + name: + type: string + annotations: + items: + $ref: '#/components/schemas/ConnectorNamespaceRequestMeta_annotations' + type: array + required: + - name + type: object + ConnectorNamespaceMeta: + allOf: + - $ref: '#/components/schemas/ObjectMeta' + - $ref: '#/components/schemas/ConnectorNamespaceRequestMeta' + ConnectorNamespaceTenant: + discriminator: + mapping: + user: '#/components/schemas/ConnectorNamespaceTenantUser' + organisation: '#/components/schemas/ConnectorNamespaceTenantOrganisation' + propertyName: kind + oneOf: + - $ref: '#/components/schemas/ConnectorNamespaceTenantUser' + - $ref: '#/components/schemas/ConnectorNamespaceTenantOrganisation' + required: + - kind + type: object + ConnectorNamespaceTenantUser: + properties: + kind: + type: string + user_id: + type: string + type: object + ConnectorNamespaceTenantOrganisation: + properties: + kind: + type: string + organisation_id: + type: string + type: object + ConnectorNamespaceRequest: + allOf: + - $ref: '#/components/schemas/ConnectorNamespaceEvalRequest' + - $ref: '#/components/schemas/ConnectorNamespaceRequest_allOf' + description: A connector namespace create request + ConnectorNamespaceEvalRequest: + allOf: + - $ref: '#/components/schemas/ConnectorNamespaceRequestMeta' + description: An evaluation connector namespace create request + ConnectorNamespaceList: + allOf: + - $ref: '#/components/schemas/List' + - $ref: '#/components/schemas/ConnectorNamespaceList_allOf' + ConnectorNamespace: + allOf: + - $ref: '#/components/schemas/ObjectReference' + - $ref: '#/components/schemas/ConnectorNamespaceMeta' + - $ref: '#/components/schemas/ConnectorNamespace_allOf' + description: A connector namespace Error_allOf: properties: code: @@ -1466,6 +1952,46 @@ components: items: $ref: '#/components/schemas/ConnectorType' type: array + ConnectorNamespaceRequestMeta_annotations: + properties: + name: + type: string + value: + type: string + required: + - name + - value + ConnectorNamespaceRequest_allOf: + properties: + cluster_id: + type: string + required: + - name + ConnectorNamespaceList_allOf: + properties: + items: + items: + $ref: '#/components/schemas/ConnectorNamespace' + type: array + ConnectorNamespace_allOf: + properties: + name: + type: string + version: + format: int64 + type: integer + cluster_id: + type: string + expiration: + type: string + tenant: + $ref: '#/components/schemas/ConnectorNamespaceTenant' + required: + - cluster_id + - id + - name + - tenant + - version securitySchemes: Bearer: bearerFormat: JWT diff --git a/internal/connector/internal/api/public/api_connector_namespaces.go b/internal/connector/internal/api/public/api_connector_namespaces.go new file mode 100644 index 000000000..6ec20088f --- /dev/null +++ b/internal/connector/internal/api/public/api_connector_namespaces.go @@ -0,0 +1,693 @@ +/* + * Connector Service Fleet Manager + * + * Connector Service Fleet Manager is a Rest API to manage connectors. + * + * API version: 0.1.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package public + +import ( + _context "context" + "github.com/antihax/optional" + _ioutil "io/ioutil" + _nethttp "net/http" + _neturl "net/url" + "strings" +) + +// Linger please +var ( + _ _context.Context +) + +// ConnectorNamespacesApiService ConnectorNamespacesApi service +type ConnectorNamespacesApiService service + +/* +CreateConnectorNamespace Create a new connector namespace +Create a new connector namespace + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param connectorNamespaceRequest Connector namespace data +@return ConnectorNamespace +*/ +func (a *ConnectorNamespacesApiService) CreateConnectorNamespace(ctx _context.Context, connectorNamespaceRequest ConnectorNamespaceRequest) (ConnectorNamespace, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodPost + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ConnectorNamespace + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/api/connector_mgmt/v1/kafka_connector_namespaces" + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = &connectorNamespaceRequest + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(r) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +/* +CreateEvaluationNamespace Create a new short lived evaluation connector namespace +Create a new evaluation connector namespace + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param connectorNamespaceEvalRequest Connector namespace data +@return ConnectorNamespace +*/ +func (a *ConnectorNamespacesApiService) CreateEvaluationNamespace(ctx _context.Context, connectorNamespaceEvalRequest ConnectorNamespaceEvalRequest) (ConnectorNamespace, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodPost + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ConnectorNamespace + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/api/connector_mgmt/v1/kafka_connector_namespaces/eval" + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = &connectorNamespaceEvalRequest + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(r) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +/* +DeleteConnectorNamespace Delete a connector namespace +Delete a connector namespace + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param connectorNamespaceId The id of the connector namespace +@return Error +*/ +func (a *ConnectorNamespacesApiService) DeleteConnectorNamespace(ctx _context.Context, connectorNamespaceId string) (Error, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodDelete + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue Error + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/api/connector_mgmt/v1/kafka_connector_namespaces/{connector_namespace_id}" + localVarPath = strings.Replace(localVarPath, "{"+"connector_namespace_id"+"}", _neturl.QueryEscape(parameterToString(connectorNamespaceId, "")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(r) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +/* +GetConnectorNamespace Get a connector namespace +Get a connector namespace + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param connectorNamespaceId The id of the connector namespace +@return ConnectorNamespace +*/ +func (a *ConnectorNamespacesApiService) GetConnectorNamespace(ctx _context.Context, connectorNamespaceId string) (ConnectorNamespace, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodGet + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ConnectorNamespace + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/api/connector_mgmt/v1/kafka_connector_namespaces/{connector_namespace_id}" + localVarPath = strings.Replace(localVarPath, "{"+"connector_namespace_id"+"}", _neturl.QueryEscape(parameterToString(connectorNamespaceId, "")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(r) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +// ListConnectorNamespacesOpts Optional parameters for the method 'ListConnectorNamespaces' +type ListConnectorNamespacesOpts struct { + Page optional.String + Size optional.String + OrderBy optional.String + Search optional.String +} + +/* +ListConnectorNamespaces Returns a list of connector namespaces +Returns a list of connector namespaces + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param optional nil or *ListConnectorNamespacesOpts - Optional Parameters: + * @param "Page" (optional.String) - Page index + * @param "Size" (optional.String) - Number of items in each page + * @param "OrderBy" (optional.String) - Specifies the order by criteria. The syntax of this parameter is similar to the syntax of the `order by` clause of an SQL statement. Each query can be ordered by any of the `ConnectorType` fields. For example, to return all Connector types ordered by their name, use the following syntax: ```sql name asc ``` To return all Connector types ordered by their name _and_ version, use the following syntax: ```sql name asc, version asc ``` If the parameter isn't provided, or if the value is empty, then the results are ordered by name. + * @param "Search" (optional.String) - Search criteria. The syntax of this parameter is similar to the syntax of the `where` clause of a SQL statement. Allowed fields in the search are `name`, `description`, `version`, `label`, and `channel`. Allowed operators are `<>`, `=`, or `LIKE`. Allowed conjunctive operators are `AND` and `OR`. However, you can use a maximum of 10 conjunctions in a search query. Examples: To return a Connector Type with the name `aws-sqs-source` and the channel `stable`, use the following syntax: ``` name = aws-sqs-source and channel = stable ```[p-] To return a Kafka instance with a name that starts with `aws`, use the following syntax: ``` name like aws%25 ``` If the parameter isn't provided, or if the value is empty, then all the Connector Type that the user has permission to see are returned. Note. If the query is invalid, an error is returned. +@return ConnectorNamespaceList +*/ +func (a *ConnectorNamespacesApiService) ListConnectorNamespaces(ctx _context.Context, localVarOptionals *ListConnectorNamespacesOpts) (ConnectorNamespaceList, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodGet + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ConnectorNamespaceList + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/api/connector_mgmt/v1/kafka_connector_namespaces" + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + if localVarOptionals != nil && localVarOptionals.Page.IsSet() { + localVarQueryParams.Add("page", parameterToString(localVarOptionals.Page.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.Size.IsSet() { + localVarQueryParams.Add("size", parameterToString(localVarOptionals.Size.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.OrderBy.IsSet() { + localVarQueryParams.Add("orderBy", parameterToString(localVarOptionals.OrderBy.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.Search.IsSet() { + localVarQueryParams.Add("search", parameterToString(localVarOptionals.Search.Value(), "")) + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(r) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +/* +UpdateConnectorNamespaceById udpate a connector namespace +udpate a connector namespace + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param connectorNamespaceId The id of the connector namespace + * @param connectorNamespaceRequest Data to updated connector with +*/ +func (a *ConnectorNamespacesApiService) UpdateConnectorNamespaceById(ctx _context.Context, connectorNamespaceId string, connectorNamespaceRequest ConnectorNamespaceRequest) (*_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodPut + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/api/connector_mgmt/v1/kafka_connector_namespaces/{connector_namespace_id}" + localVarPath = strings.Replace(localVarPath, "{"+"connector_namespace_id"+"}", _neturl.QueryEscape(parameterToString(connectorNamespaceId, "")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = &connectorNamespaceRequest + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(r) + if err != nil || localVarHTTPResponse == nil { + return localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + return localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + return localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarHTTPResponse, newErr + } + + return localVarHTTPResponse, nil +} diff --git a/internal/connector/internal/api/public/client.go b/internal/connector/internal/api/public/client.go index a693cd3a3..e6c6c9578 100644 --- a/internal/connector/internal/api/public/client.go +++ b/internal/connector/internal/api/public/client.go @@ -50,6 +50,8 @@ type APIClient struct { ConnectorClustersApi *ConnectorClustersApiService + ConnectorNamespacesApi *ConnectorNamespacesApiService + ConnectorServiceApi *ConnectorServiceApiService ConnectorTypesApi *ConnectorTypesApiService @@ -74,6 +76,7 @@ func NewAPIClient(cfg *Configuration) *APIClient { // API Services c.ConnectorClustersApi = (*ConnectorClustersApiService)(&c.common) + c.ConnectorNamespacesApi = (*ConnectorNamespacesApiService)(&c.common) c.ConnectorServiceApi = (*ConnectorServiceApiService)(&c.common) c.ConnectorTypesApi = (*ConnectorTypesApiService)(&c.common) c.ConnectorsApi = (*ConnectorsApiService)(&c.common) diff --git a/internal/connector/internal/api/public/model_connector_namespace.go b/internal/connector/internal/api/public/model_connector_namespace.go new file mode 100644 index 000000000..39ede17b4 --- /dev/null +++ b/internal/connector/internal/api/public/model_connector_namespace.go @@ -0,0 +1,30 @@ +/* + * Connector Service Fleet Manager + * + * Connector Service Fleet Manager is a Rest API to manage connectors. + * + * API version: 0.1.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package public + +import ( + "time" +) + +// ConnectorNamespace A connector namespace +type ConnectorNamespace struct { + Id string `json:"id"` + Kind string `json:"kind,omitempty"` + Href string `json:"href,omitempty"` + Owner string `json:"owner,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty"` + ModifiedAt time.Time `json:"modified_at,omitempty"` + Name string `json:"name"` + Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` + Version int64 `json:"version"` + ClusterId string `json:"cluster_id"` + Expiration string `json:"expiration,omitempty"` + Tenant ConnectorNamespaceTenant `json:"tenant"` +} diff --git a/internal/connector/internal/api/public/model_connector_namespace_eval_request.go b/internal/connector/internal/api/public/model_connector_namespace_eval_request.go new file mode 100644 index 000000000..3941867e0 --- /dev/null +++ b/internal/connector/internal/api/public/model_connector_namespace_eval_request.go @@ -0,0 +1,16 @@ +/* + * Connector Service Fleet Manager + * + * Connector Service Fleet Manager is a Rest API to manage connectors. + * + * API version: 0.1.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package public + +// ConnectorNamespaceEvalRequest An evaluation connector namespace create request +type ConnectorNamespaceEvalRequest struct { + Name string `json:"name"` + Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` +} diff --git a/internal/connector/internal/api/public/model_connector_namespace_list.go b/internal/connector/internal/api/public/model_connector_namespace_list.go new file mode 100644 index 000000000..f675524f0 --- /dev/null +++ b/internal/connector/internal/api/public/model_connector_namespace_list.go @@ -0,0 +1,19 @@ +/* + * Connector Service Fleet Manager + * + * Connector Service Fleet Manager is a Rest API to manage connectors. + * + * API version: 0.1.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package public + +// ConnectorNamespaceList struct for ConnectorNamespaceList +type ConnectorNamespaceList struct { + Kind string `json:"kind"` + Page int32 `json:"page"` + Size int32 `json:"size"` + Total int32 `json:"total"` + Items []ConnectorNamespace `json:"items"` +} diff --git a/internal/connector/internal/api/public/model_connector_namespace_meta.go b/internal/connector/internal/api/public/model_connector_namespace_meta.go new file mode 100644 index 000000000..44edea726 --- /dev/null +++ b/internal/connector/internal/api/public/model_connector_namespace_meta.go @@ -0,0 +1,23 @@ +/* + * Connector Service Fleet Manager + * + * Connector Service Fleet Manager is a Rest API to manage connectors. + * + * API version: 0.1.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package public + +import ( + "time" +) + +// ConnectorNamespaceMeta struct for ConnectorNamespaceMeta +type ConnectorNamespaceMeta struct { + Owner string `json:"owner,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty"` + ModifiedAt time.Time `json:"modified_at,omitempty"` + Name string `json:"name"` + Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` +} diff --git a/internal/connector/internal/api/public/model_connector_namespace_request.go b/internal/connector/internal/api/public/model_connector_namespace_request.go new file mode 100644 index 000000000..f8c0445cf --- /dev/null +++ b/internal/connector/internal/api/public/model_connector_namespace_request.go @@ -0,0 +1,17 @@ +/* + * Connector Service Fleet Manager + * + * Connector Service Fleet Manager is a Rest API to manage connectors. + * + * API version: 0.1.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package public + +// ConnectorNamespaceRequest A connector namespace create request +type ConnectorNamespaceRequest struct { + Name string `json:"name"` + Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` + ClusterId string `json:"cluster_id,omitempty"` +} diff --git a/internal/connector/internal/api/public/model_connector_namespace_request_meta.go b/internal/connector/internal/api/public/model_connector_namespace_request_meta.go new file mode 100644 index 000000000..6720545ac --- /dev/null +++ b/internal/connector/internal/api/public/model_connector_namespace_request_meta.go @@ -0,0 +1,16 @@ +/* + * Connector Service Fleet Manager + * + * Connector Service Fleet Manager is a Rest API to manage connectors. + * + * API version: 0.1.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package public + +// ConnectorNamespaceRequestMeta struct for ConnectorNamespaceRequestMeta +type ConnectorNamespaceRequestMeta struct { + Name string `json:"name"` + Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` +} diff --git a/internal/connector/internal/api/public/model_connector_namespace_request_meta_annotations.go b/internal/connector/internal/api/public/model_connector_namespace_request_meta_annotations.go new file mode 100644 index 000000000..e8254842a --- /dev/null +++ b/internal/connector/internal/api/public/model_connector_namespace_request_meta_annotations.go @@ -0,0 +1,16 @@ +/* + * Connector Service Fleet Manager + * + * Connector Service Fleet Manager is a Rest API to manage connectors. + * + * API version: 0.1.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package public + +// ConnectorNamespaceRequestMetaAnnotations struct for ConnectorNamespaceRequestMetaAnnotations +type ConnectorNamespaceRequestMetaAnnotations struct { + Name string `json:"name"` + Value string `json:"value"` +} diff --git a/internal/connector/internal/api/public/model_connector_namespace_target.go b/internal/connector/internal/api/public/model_connector_namespace_target.go new file mode 100644 index 000000000..40c09d0c7 --- /dev/null +++ b/internal/connector/internal/api/public/model_connector_namespace_target.go @@ -0,0 +1,16 @@ +/* + * Connector Service Fleet Manager + * + * Connector Service Fleet Manager is a Rest API to manage connectors. + * + * API version: 0.1.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package public + +// ConnectorNamespaceTarget Targets workloads to an addon cluster namespace +type ConnectorNamespaceTarget struct { + Kind string `json:"kind"` + NamespaceId string `json:"namespace_id,omitempty"` +} diff --git a/internal/connector/internal/api/public/model_connector_namespace_tenant.go b/internal/connector/internal/api/public/model_connector_namespace_tenant.go new file mode 100644 index 000000000..982d44848 --- /dev/null +++ b/internal/connector/internal/api/public/model_connector_namespace_tenant.go @@ -0,0 +1,17 @@ +/* + * Connector Service Fleet Manager + * + * Connector Service Fleet Manager is a Rest API to manage connectors. + * + * API version: 0.1.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package public + +// ConnectorNamespaceTenant struct for ConnectorNamespaceTenant +type ConnectorNamespaceTenant struct { + Kind string `json:"kind"` + UserId string `json:"user_id,omitempty"` + OrganisationId string `json:"organisation_id,omitempty"` +} diff --git a/internal/connector/internal/api/public/model_connector_namespace_tenant_organisation.go b/internal/connector/internal/api/public/model_connector_namespace_tenant_organisation.go new file mode 100644 index 000000000..10dbdc7ea --- /dev/null +++ b/internal/connector/internal/api/public/model_connector_namespace_tenant_organisation.go @@ -0,0 +1,16 @@ +/* + * Connector Service Fleet Manager + * + * Connector Service Fleet Manager is a Rest API to manage connectors. + * + * API version: 0.1.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package public + +// ConnectorNamespaceTenantOrganisation struct for ConnectorNamespaceTenantOrganisation +type ConnectorNamespaceTenantOrganisation struct { + Kind string `json:"kind,omitempty"` + OrganisationId string `json:"organisation_id,omitempty"` +} diff --git a/internal/connector/internal/api/public/model_connector_namespace_tenant_user.go b/internal/connector/internal/api/public/model_connector_namespace_tenant_user.go new file mode 100644 index 000000000..3b936c491 --- /dev/null +++ b/internal/connector/internal/api/public/model_connector_namespace_tenant_user.go @@ -0,0 +1,16 @@ +/* + * Connector Service Fleet Manager + * + * Connector Service Fleet Manager is a Rest API to manage connectors. + * + * API version: 0.1.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package public + +// ConnectorNamespaceTenantUser struct for ConnectorNamespaceTenantUser +type ConnectorNamespaceTenantUser struct { + Kind string `json:"kind,omitempty"` + UserId string `json:"user_id,omitempty"` +} diff --git a/internal/connector/internal/api/public/model_deployment_location.go b/internal/connector/internal/api/public/model_deployment_location.go index 42807f5e0..bedb7a43d 100644 --- a/internal/connector/internal/api/public/model_deployment_location.go +++ b/internal/connector/internal/api/public/model_deployment_location.go @@ -11,6 +11,7 @@ package public // DeploymentLocation struct for DeploymentLocation type DeploymentLocation struct { - Kind string `json:"kind"` - ClusterId string `json:"cluster_id,omitempty"` + Kind string `json:"kind"` + ClusterId string `json:"cluster_id,omitempty"` + NamespaceId string `json:"namespace_id,omitempty"` } diff --git a/internal/connector/internal/config/connectors.go b/internal/connector/internal/config/connectors.go index 2dfd0a3dc..220e50703 100644 --- a/internal/connector/internal/config/connectors.go +++ b/internal/connector/internal/config/connectors.go @@ -13,15 +13,21 @@ import ( "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/shared" "github.com/golang/glog" "github.com/spf13/pflag" + "time" ) type ConnectorsConfig struct { - ConnectorCatalogDirs []string `json:"connector_types"` - CatalogEntries []ConnectorCatalogEntry `json:"connector_type_urls"` + ConnectorEvalDurationString string `json:"connector_eval_duration"` + ConnectorEvalOrganizations []string `json:"connector_eval_organizations"` + ConnectorCatalogDirs []string `json:"connector_types"` + CatalogEntries []ConnectorCatalogEntry `json:"connector_type_urls"` + ConnectorEvalDuration time.Duration } var _ environments.ConfigModule = &ConnectorsConfig{} +var _ environments.ServiceValidator = &ConnectorsConfig{} + type ConnectorChannelConfig struct { Revision int64 `json:"revision,omitempty"` ShardMetadata map[string]interface{} `json:"shard_metadata,omitempty"` @@ -38,6 +44,8 @@ func NewConnectorsConfig() *ConnectorsConfig { func (c *ConnectorsConfig) AddFlags(fs *pflag.FlagSet) { fs.StringArrayVar(&c.ConnectorCatalogDirs, "connector-catalog", c.ConnectorCatalogDirs, "Directory containing connector catalog entries") + fs.StringArrayVar(&c.ConnectorEvalOrganizations, "connector-eval-duration", c.ConnectorCatalogDirs, "Connector eval duration in go duration format") + fs.StringArrayVar(&c.ConnectorEvalOrganizations, "connector-eval-organizations", c.ConnectorCatalogDirs, "Connector eval organization IDs") } func (c *ConnectorsConfig) ReadFiles() error { @@ -85,3 +93,13 @@ func (c *ConnectorsConfig) ReadFiles() error { c.CatalogEntries = values return nil } + +func (c *ConnectorsConfig) Validate(env *environments.Env) error { + // validate duration + duration, err := time.ParseDuration(c.ConnectorEvalDurationString) + if err != nil { + return err + } + c.ConnectorEvalDuration = duration + return nil +} diff --git a/internal/connector/internal/generated/bindata.go b/internal/connector/internal/generated/bindata.go index 22b9ae7bd..3673d0340 100644 --- a/internal/connector/internal/generated/bindata.go +++ b/internal/connector/internal/generated/bindata.go @@ -78,7 +78,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _connector_mgmtYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\xff\x73\xdb\x38\xae\xf8\xef\xf9\x2b\xf8\x71\x3f\x37\xb9\x7b\x2f\x76\x6c\xc7\xf9\xe6\x79\xbd\x99\x34\x49\xbb\xd9\x4d\xd3\x6e\x92\x6e\xb7\x77\x73\xe3\xd0\x12\x6c\xb3\x91\x48\x85\xa4\x92\xba\x77\xef\x7f\x7f\x43\x52\xb2\x28\x89\xb2\x65\x27\xfd\xb6\x6b\xcf\xdc\x6d\x23\x81\x20\x08\x02\x20\x00\x82\x14\x8b\x80\xe2\x88\xf4\xd1\x4e\xab\xdd\x6a\xa3\x67\x88\x02\xf8\x48\x4e\x88\x40\x58\xa0\x11\xe1\x42\xa2\x80\x50\x40\x92\x21\x1c\x04\xec\x01\x09\x16\x02\x3a\x3b\x39\x15\xea\xd1\x2d\x65\x0f\x06\x5a\x35\xa0\x28\x41\x87\x7c\xe6\xc5\x21\x50\xd9\xda\x78\x86\x8e\x82\x00\x01\xf5\x23\x46\xa8\x14\xc8\x87\x11\xa1\xe0\xa3\x09\x70\x40\x0f\x24\x08\xd0\x10\x90\x4f\x84\xc7\xee\x81\xe3\x61\x00\x68\x38\x55\x3d\xa1\x58\x00\x17\x2d\x74\x36\x42\x52\xc3\xaa\x0e\x12\xea\x18\xba\x05\x88\x0c\x25\x33\xcc\x1b\xcf\x50\x23\xe2\xe4\x1e\x4b\x68\x6c\x21\xec\xab\x51\x40\xa8\x80\xe5\x04\x50\xc3\x63\x94\x82\x27\x19\x1f\x84\xe3\x50\x36\x13\xc8\xd6\x14\x87\x41\x03\x8d\x48\x00\x1b\x84\x8e\x58\x7f\x03\x21\x49\x64\x00\x7d\x74\x9c\x36\x40\x57\xc0\xef\x89\x07\xe8\x65\x00\x20\xd1\x6b\x4c\xf1\x18\xf8\x06\x42\xf7\xc0\x05\x61\xb4\x8f\xda\xad\x4e\xab\xbd\x81\x90\x0f\xc2\xe3\x24\x92\xfa\xe1\x82\xf6\x66\x3c\x97\x20\x24\x3a\x7a\x7b\xa6\xc8\x0c\xf5\x0b\x34\x23\x54\xb4\x36\x04\x70\xd5\x89\xa2\xaa\x89\x62\x1e\xf4\xd1\x44\xca\x48\xf4\xb7\xb7\x71\x44\x5a\x8a\xd9\x62\x42\x46\xb2\xe5\xb1\x70\x03\xa1\x02\x01\xaf\x31\xa1\xe8\xaf\x11\x67\x7e\xec\xa9\x27\x7f\x43\x06\x9d\x1b\x99\x90\x78\x0c\x8b\x50\x5e\x49\x3c\x26\x74\xec\x44\xd4\xdf\xde\x0e\x98\x87\x83\x09\x13\xb2\x7f\xd0\x6e\xb7\xcb\xcd\x67\xef\xb3\x96\xdb\x65\x28\x2f\xe6\x1c\xa8\x44\x3e\x0b\x31\xa1\x1b\x12\x8f\x13\x06\x50\x1c\xe6\xe6\xe5\x7a\x1a\x81\x28\xb7\x6f\x34\x5c\xd0\xb5\x01\xd1\x71\x10\x0b\x09\x4b\x34\x48\xe6\xd7\x09\xbf\x11\x61\x39\xd1\xf4\x3f\x53\xff\x43\xce\x66\xcf\x36\x36\x10\x6a\xa8\x69\xd8\xce\x8b\xe9\xf6\x7d\xa7\xd1\xd7\x78\xc7\x20\xcd\x3f\x10\x4a\x19\x62\x7e\xcd\x0a\x42\x90\xd2\x45\x8e\x15\x21\x67\x7e\x5f\xb5\xff\xcd\x88\xeb\x6b\x90\xd8\xc7\x12\x27\x50\x22\x0e\x43\xcc\xa7\x7d\x74\x09\x32\xe6\x54\x68\x6d\x49\x24\x1b\x85\x79\xd8\xdc\xe0\xea\x34\xe0\x20\x22\x46\x05\x58\xf4\x36\xba\xed\x76\x23\xfb\x13\x29\x79\x97\x40\xa5\xfd\x08\x21\x1c\x45\x01\xf1\x34\xf5\xdb\x1f\x05\xa3\xf9\xb7\x08\x09\x6f\x02\x21\x2e\x3e\x45\xe8\xff\x73\x18\xf5\xd1\xe6\xb3\x6d\x8f\x85\x11\xa3\x40\xa5\xd8\x36\xb0\x62\xbb\x30\xfe\x4d\xab\x71\x6e\x60\xbf\x15\xc7\x32\x9b\xbc\xb2\xe8\xcd\x9b\xb9\xed\x5b\x3c\xba\xc5\x83\xec\xb9\x54\x8d\xb6\xff\x9d\x7f\x30\x20\xfe\xff\x26\xfc\x88\x30\xc7\x21\xc8\x44\xe1\xcd\xe4\x1a\x59\x2b\x35\xd9\x70\x52\x7e\x3d\x01\x44\x7c\xc4\xb4\xc9\xcc\x1a\x21\xd5\x68\xa3\x9a\x75\xea\x75\x1f\x09\xc9\x09\x1d\xcf\x1e\x13\xda\x47\x4a\x76\x67\x0f\x38\xdc\xc5\x84\x83\xdf\x47\x92\xc7\x50\x5f\x28\x33\x2d\x45\x48\x80\x17\x73\x22\xa7\x36\xe4\x0b\xc0\x1c\x78\x1f\xfd\x13\xfd\xab\x42\x70\x67\xb8\x14\xaa\x17\xd3\xb3\x93\xa2\xe8\xbe\x02\x89\x70\x61\xbc\x6a\x19\x99\xf1\x29\x2f\xb8\x0b\xc1\xbf\x91\xd8\x36\x9c\x62\x9b\x1b\x7d\xa3\xd0\x14\x3e\xe1\x30\x0a\x6c\x42\xd3\x5f\xae\xd9\xa9\x01\x2b\x43\xb9\xbb\x4e\xb1\x6e\xbb\x90\x34\xaa\xf4\xe6\xba\x24\x73\x28\xc4\xd2\x9b\xa8\x05\x43\xc9\xa3\x12\x20\xd0\xb6\x3f\x61\x69\xaf\xdd\xf9\x36\x2c\x3d\xe5\x9c\xf1\xfa\xac\xec\xb5\x3b\xab\x32\x30\x6b\x5a\xc9\xb6\xa3\x58\x4e\x90\x64\xb7\x40\x95\x4b\x40\xe8\x3d\x0e\x2c\xfd\x6e\xf4\xda\xbd\x1f\x84\x49\xbd\xd5\x99\xd4\x5b\xc4\xa4\x0b\x96\xc9\x52\x41\xc6\xe0\x13\x11\x52\x64\x0c\xdb\xfd\x56\x8a\xba\x24\xc3\x76\xdb\xed\x55\x19\x96\x35\xad\x64\xd8\x3b\x0a\x9f\x22\xf0\x24\xf8\x08\x14\x5d\x88\x79\xda\xaf\xf2\x97\x5e\xb0\x96\x71\x40\x9e\xd8\xd6\x8b\x2a\x1f\x05\xa3\x80\x08\xa9\x16\xba\xbc\x30\x08\x97\xbd\xaf\xdb\xa8\xbc\xfc\x2a\x92\x5d\x13\x91\x41\x6e\x47\x78\x6c\x4d\xc2\x42\x70\x41\x3e\x2f\x03\xce\xb8\x0f\xfc\xc5\x74\x99\x0e\x00\x73\x6f\xd2\xf8\xee\x17\xb2\x73\x22\x64\xb5\x49\x5c\x30\x53\xeb\xb5\xa3\xde\xda\xb1\x36\x85\x0b\x4d\x61\xc1\xb1\x5f\xd2\xa5\x4f\x8d\x63\xa4\x62\xde\x45\xd6\xf1\x11\x86\xd1\xe3\x80\x25\xd8\x54\x22\xdb\x2c\x1e\xeb\xd7\x3a\x3d\xf2\x90\xa9\x8c\xcb\x16\xce\x85\x74\x1b\x40\x15\x08\xdc\xc5\xc0\xa7\x16\x7f\x4d\x54\x82\xc5\x94\x7a\x55\x5c\x7f\x0b\x7c\xc4\x78\xa8\x3d\x3f\xac\xf3\x0f\x88\x50\x84\xa9\x69\x35\xe1\x8c\xb2\x58\xa0\x10\x53\x0a\x7c\x63\xbe\xb4\x99\xf8\x64\xc8\x58\x00\x98\x5a\x6f\x1c\x11\x09\x4a\xbd\xcc\x17\xcc\xb7\x18\x5c\x91\x98\xb1\x22\x55\xa7\x72\xcc\x57\x0d\xb7\x62\xd4\xb2\x80\x97\x86\xc8\xbc\x86\x54\xe9\xc7\xac\x95\x99\xbc\x4a\x4d\xa9\xe7\xc9\xe7\x90\x34\xe6\x45\x77\x55\xcb\x47\xf7\x1b\x2f\x1f\xd5\xd6\xd0\xf3\x20\x92\x90\x73\x9e\x7f\x0c\x03\xd8\x6b\xb7\xf5\xbc\x10\x46\x57\x5f\x2d\x8a\x28\x2a\xf9\xf4\x9b\x5a\x25\x34\xa4\x31\x88\x22\xb3\x88\x16\xe7\xd6\xeb\xeb\x3a\x36\xab\x15\x9b\x5d\x67\xb1\x3d\xf8\xca\x66\xb0\x98\x7b\x80\x7c\x06\x82\x6e\x4a\x13\x9f\xad\x7d\x92\x82\x60\x51\x14\x57\xb9\x25\x66\xb5\x4f\xb3\x26\xf9\x45\xba\x4e\x14\xf6\x08\x3f\x43\xb9\xdd\x65\x3c\x7f\xb0\xe8\xeb\xbb\x8e\x8d\x96\x8d\x8b\xd6\x21\xd1\x3a\x24\xfa\x36\xd9\x21\xb1\xfd\xef\xf9\x5b\x17\x0b\x94\x91\xf8\x8d\xaf\x61\xd2\xec\x9c\x52\xd1\xa0\x15\x36\x02\x5c\xe6\xcb\x0d\xf2\x7d\xda\x8e\x9a\x99\xf9\x75\x52\x7e\xed\xf8\xd5\x61\xd2\x3a\x29\xbf\x14\xc3\x1e\x65\x76\x0d\x68\x00\x12\xbe\xa4\x2d\x34\x3d\x54\x9a\xc3\x13\xfd\x7a\x91\x45\xac\x84\x72\x1b\xc5\xef\x45\x51\x1c\x63\x58\x87\xbb\x7f\x58\xab\x67\x26\xf8\x11\xb6\x2f\x87\x60\x9e\x05\xd4\x5e\x51\xba\x8c\xa2\x07\x22\x27\x48\x44\xe0\x91\x11\x01\x1f\x9d\x9d\xfc\xc8\x96\xf0\x71\x4c\x2c\x22\x58\xd1\x2a\x46\x6a\x85\xf9\x92\x46\x51\x77\x50\x69\x13\xdf\xaa\xb7\x8b\x4c\x62\x15\xd0\xe2\x5c\xf4\x09\x96\x18\x49\x66\x88\x28\x54\xed\x28\x59\xda\x98\x23\x26\xb6\x90\x84\xc0\xc7\xd0\xd4\x58\xfe\xbb\x6e\xa6\xda\xa4\xd5\xd9\xf0\x23\x78\xb2\x02\xad\x42\xb5\x24\xd6\x42\xc0\xfa\xf3\xd5\x9b\x0b\xc3\x9f\x2d\x74\xf9\xf2\x18\xed\x1d\xb6\xbb\xa8\x39\xab\x3c\x94\x8c\x05\xa2\x45\x40\x8e\x5a\x8c\x8f\xb7\x27\x32\x0c\xb6\xf9\xc8\x53\x50\xab\x51\xfb\x25\x52\xf4\xb3\xe6\x7f\x84\x24\xf9\x3a\x16\xf8\xf3\xae\x8a\xeb\x58\xe0\x47\x88\x05\x1c\xe5\xa6\x49\x49\xf2\xb2\x05\xa7\x5e\x52\xc9\xbc\xcc\x2e\x75\xbe\xfc\x79\xfe\x3e\x74\x46\x16\xaa\xbd\xf6\x2e\xd8\xb4\x46\x5e\x0e\x67\x8d\xcd\xeb\x42\x8b\x3f\xdd\x26\x76\x32\xfc\x6f\xb7\x99\x9d\x48\xc1\x8a\x7b\xda\xa6\xf1\xd3\x6c\x6d\x3b\x70\xfd\x90\x3b\xdc\xc9\x40\xd6\x1b\xdd\xeb\x8d\x6e\x8b\x73\x6b\x1f\xa7\x06\x93\xd6\x1b\xdd\xdf\x97\x9b\xb3\xc2\x46\x77\x6e\x41\xaf\x55\x76\x5c\x70\x59\x1e\xbb\xf1\x5d\x44\x57\x67\xff\xdb\xcb\xb7\xa9\xbd\x05\x5e\x68\xf7\xb5\x6b\x90\xbf\xcf\x8d\xac\x64\x02\x96\xae\x11\x2e\x30\x73\x6d\xdd\xd7\x7b\xe2\x5f\xf9\xc4\x44\x2a\x81\xf6\x29\xbf\xe4\xd9\x92\x07\xfd\xb2\x56\xee\x00\xa0\xea\xac\x5f\x3e\x1a\xfa\xfa\xc7\xfd\x1e\x6f\x8b\xed\x1d\xfb\x42\x84\x59\x75\xe0\x6f\x4e\xd0\x38\x1f\xf4\xbb\xb6\x7f\x35\x73\x78\x69\x00\xb8\xce\xe5\xad\xfd\xdc\x3a\x4c\x5a\x31\x97\x97\x8a\xd9\x3a\xa7\xb7\xea\x4e\x56\xfc\x55\xcc\x67\x1c\xf9\x8e\x1c\xdd\x8b\xe9\x99\x5f\xb4\xa2\xb1\x1f\xe1\xfc\x4e\xfe\x3c\x43\xba\x10\xba\xfe\x6e\x97\x21\xd1\x5f\x71\xaf\xeb\xab\x24\xaf\x96\xc8\x16\xe5\x4d\x46\x3e\x4b\x97\xe8\x8c\x90\x58\xc6\xfa\x8e\x94\x64\xe8\x6b\xbb\xbc\xb6\xcb\x4f\x6c\x97\xd7\x26\xd9\xc5\xb3\x27\x29\xb9\x7a\x02\xab\x5c\x28\xbd\xaa\xf0\x6b\xcb\xb5\x55\xf3\x2c\xf2\x42\xe8\x75\x45\xd6\xda\x2e\x7e\x27\x4c\xfa\x8a\x15\x59\xb3\xc4\xec\xba\x18\xeb\x29\x8b\xb1\x9e\x2e\x0b\xb2\x8d\x7d\x9f\xd1\x41\x96\x05\x59\xa7\x45\x56\x4b\x8b\x1c\x29\x3e\xbe\x9d\x71\xad\xb8\x9a\x54\xa4\x3e\x36\x05\xd2\x13\x60\xf1\xdb\xb5\xba\x2c\xdd\xfa\xbb\xca\xa5\xe4\x59\x33\x37\x93\xac\x44\x26\x1b\x0c\x92\x13\x2c\x91\x98\xb0\x38\xf0\xd1\x10\x50\x2c\xcc\x8d\x83\x1e\xa3\x23\x32\x8e\x39\x68\xc1\x32\x77\xf5\xd9\x11\x8c\x61\x0a\xa3\x46\xee\x0c\xaf\x5a\xeb\xe5\xec\x8f\xba\x9c\xad\xd3\x2f\x3f\x92\xaf\xbf\x91\x61\x54\x1d\x27\xd4\xf7\xcd\x51\xd0\x67\xe6\xff\xd1\x31\x0b\x43\x46\x93\x47\xfa\x3f\xca\x6c\xf4\x67\xd6\x2d\x31\xfc\x96\xc5\xbe\x25\xd4\xb7\xfe\x8c\xf0\x18\xac\x3f\x05\xf9\x6c\xff\x29\x99\xc4\x81\xf5\x37\x91\x10\xa6\x53\xe8\x28\x6e\x8d\xb8\xb2\xfe\x92\xd8\x6c\x54\xfd\x2d\x5c\xb2\x14\x15\x65\x20\x42\x25\x8c\xed\xf5\x8f\x7c\xae\x01\xa5\x69\xae\x06\xd3\x2f\xb4\x08\xa4\x30\x38\x08\xde\x8c\x6c\x16\xcd\x13\x9e\x37\x7a\xbc\x97\x30\x02\x0e\xd4\xcb\x6d\x61\x56\x54\xfb\xba\x98\x82\xb4\xbc\xfb\x25\x89\x72\x32\x07\xe9\x99\xc4\x0e\xe9\xaf\x04\x9f\x2d\xc2\x03\xe2\xcf\x6d\xa4\xdf\x15\xc6\xd4\x5f\x6e\x82\xc9\xe2\xe9\xad\x25\x03\x13\xc5\xf5\x2a\x20\x8b\xce\xd7\x20\xf1\x92\x24\xb2\x07\x0a\x7c\x21\x01\xa6\x50\xd0\x1f\xe0\x9c\x0d\x1a\x31\x1e\x62\xd9\x47\x3e\x96\xd0\x94\x24\x84\x45\x68\x42\xe6\x6b\xdf\x7d\x55\x3c\xfa\x79\x72\x2b\x6a\xe2\x3c\x11\x46\xaf\x40\x4a\x42\x33\x4f\xcd\xa5\xda\xc4\x56\xec\x98\x07\x8f\x9b\xb4\x98\x3b\xb4\xc8\x41\xe3\x91\xe7\xb1\x98\xce\xb5\x39\x5e\x40\x80\xca\x41\x8e\xbe\xe4\x99\x00\x8f\xc3\xbc\xb9\x9b\xb5\x5d\x3c\x7f\x36\xc6\xf9\xa4\x9f\x40\x14\xb0\x69\x08\x54\x9e\x33\xb3\xba\xa4\xf0\x3e\x51\xc6\x39\x24\x14\x4b\x66\x89\x4c\x42\xd9\xf4\x42\xbb\xf6\x39\x1b\x1a\xe2\x28\x22\x74\x6c\x77\x58\xf4\x79\xeb\x66\x74\xaf\x31\x1f\xc3\xcc\xe9\x63\x14\xea\xdb\xa5\x2a\x54\x1b\x2e\x7a\xcc\xcb\xbe\xcb\x83\x6e\x98\x77\x02\x3d\x30\x7e\x1b\x30\xec\xeb\x2b\xb3\x31\x4d\x7c\x45\x2f\xbf\xcb\xe7\xd0\xbf\x05\x6b\xce\xca\x4b\x44\x16\x44\xcd\x9f\xda\xc2\x8d\xb9\x5f\xc9\xc8\x83\xcb\x43\xd0\xe3\x42\x8d\xa3\xb7\x67\x09\x51\x79\xa7\x83\xa8\x97\xf7\x9d\xfc\xc3\x89\x21\xab\xe2\x5e\xe5\xc2\x02\x12\x04\xc6\x38\x94\xbc\x96\xa6\x41\xae\x63\x5c\x51\x74\x75\x16\x74\x52\xbe\x2f\xac\xd4\x3e\x19\x58\xe5\x05\x10\xd5\x4b\x5e\x25\xc5\x86\xaf\x98\x73\x3c\x2d\xbc\xd1\x3e\x47\xd9\xf5\x2a\x4c\xa8\x3d\xf6\xa5\xa6\x36\xe7\x4e\x25\x26\x4d\xd8\x0e\xd5\x2f\x8a\x1d\xd5\x86\x38\xa7\x3d\x3f\xb1\xc0\x17\x69\x14\xaf\x43\x2f\x53\xc9\x69\x62\x31\x85\x41\x6b\x93\xc1\x89\xce\xa8\x90\x98\x7a\xd0\x5a\x45\x46\x2b\x57\x88\x6c\x22\x9e\x25\x07\xfd\x92\x74\x92\x67\xcd\x4b\x06\x53\x21\xd2\xcf\xf2\xb3\x68\x0c\xbe\xee\xfa\x12\xc6\x44\x48\x3e\x7d\x62\x96\x68\xe4\x28\x45\xfe\x15\x78\x63\x80\x11\x4f\x7b\x7c\x2a\x2e\xa5\xb2\xa4\xa3\xf9\x9c\x24\xe5\xe3\x7b\xb7\xf9\x3d\x2a\x66\x2a\xe6\x98\xda\x15\x17\xf6\x7b\x1c\xc4\x0e\x3f\xda\x36\xa2\xe5\x4c\x44\x15\xb5\x69\x41\x5b\x31\xbf\x92\x27\xdb\xd6\xeb\x82\x3e\xd7\x4f\x88\x34\x8a\xa1\x4f\xf9\xa4\xc9\x8c\xd5\xc5\x15\xef\x4a\x62\x59\x70\x6c\x73\x5c\x01\x1a\x87\xb6\x74\x29\x37\xc0\xa0\x00\xdb\x69\xe1\x80\xfd\xa9\xbb\x87\x64\x37\xd6\xf6\x4e\x5d\xf3\xa3\x73\x83\x73\x79\x5f\x81\xd8\x3d\x01\x46\x25\x95\x73\x69\x57\xd0\x64\xfb\xd5\x08\xeb\x33\x16\x28\x0a\x30\x05\x2b\x1d\x66\x36\x77\x1b\xab\x28\xd7\x9c\x81\x57\xb8\x1b\x36\x4f\x56\x58\x87\x0d\xe6\x2f\x45\xdc\x95\xe6\xc4\xbc\x29\x13\x39\x88\x0a\x5d\xac\x6a\x9c\x22\x28\x85\x7a\xcb\x8c\x42\x4b\x6f\xe1\x90\x8a\x1d\xc1\xd6\x17\xa6\xa7\x76\x87\x96\x19\xc5\x63\xe6\xf1\x2a\x91\x57\xe7\xa0\x6c\xfb\xb4\xd4\xc0\xf2\x7e\xcb\xd2\x11\xbc\xd3\x33\x59\xda\x91\x59\xae\xba\xce\x6d\x02\xad\xa7\xc7\x13\x4c\x29\x04\x73\x6c\x9d\x0f\x23\x1c\x07\x52\x3d\xc5\xc3\x00\x2a\x2c\x60\xf2\x32\xcf\xf0\x13\x10\xca\xb7\x5f\xd6\x9a\x1a\xb3\x69\xe3\x66\x51\x94\x33\xac\x7e\xb2\x95\x9a\xef\x6e\xd9\x7e\xb0\x10\x64\x4c\xed\xb5\x2e\x7d\x96\xeb\x4c\x9b\xc6\x3c\xd4\x62\x0a\x47\x98\x04\x65\x92\xf3\x58\xfc\xc2\x86\x70\x53\x89\xce\x3d\x51\xae\x7f\x11\x30\xf7\xa2\x20\xd5\xb6\x9f\x34\x37\x95\xa7\xbc\x3b\x9b\x68\xe3\xf6\x0c\xb0\x89\xc8\xad\x37\xa5\x5b\x73\x5d\x51\x98\xc2\x66\x8b\xe7\x3c\xc1\xac\x70\x8a\x33\x65\x2a\xd0\x52\xc6\xeb\xfe\xde\x49\x3e\xa7\x90\x7d\xee\xc4\xbc\x1f\xa4\xce\x5a\x5d\x32\x17\x79\xac\x19\xbd\x33\x0e\xd9\xa8\x9f\x59\xb9\xec\x79\xee\xa1\x82\xd4\xcb\xac\x98\xe0\x08\x72\x8f\x23\xce\x3c\x10\xc2\xbe\xf4\x4e\x3d\x36\xb9\xde\x09\xa6\x7e\x90\xcf\xdd\xe5\x4c\x50\x5e\x2e\x1c\x1e\x86\x4b\x2a\x94\x87\xe1\x9a\xfa\xd2\x67\x58\xb4\x18\x26\x69\x90\x41\x90\xe4\x41\x72\x6f\xb5\xb2\x0f\xf4\xf2\xb5\xaa\x4b\x53\xe2\x6f\x4a\xc6\xe2\x16\x79\x43\xb6\x68\xaa\x13\xbb\x97\xcd\xa8\x63\x70\x75\x71\x95\xd3\x43\x36\x5a\x8b\x2b\xb5\x89\x73\x19\xd0\xe2\x6a\x56\x70\xf4\x56\x73\xca\x72\x0e\xcf\x92\x6d\x73\x86\xa7\x48\xdd\xb7\x70\xe2\x2a\x06\xb3\xe4\x32\x9d\xd6\x56\x0c\xd2\x6f\x9f\x39\x57\xec\xe2\x36\x82\xf9\xa5\x59\x5b\x42\xe5\x5e\xcf\xb1\x3a\x7d\xb7\x9e\xe3\x13\xb8\x8c\xdf\xc4\x57\x7c\x0a\xc1\x5d\xb2\xb5\xdb\xb7\xfc\x13\x38\x95\x0d\xb7\x33\x69\x7d\x11\xa5\x18\x4d\xab\x37\xce\x40\xf4\xef\xcd\x19\x09\x97\x10\x71\x10\xaa\xc7\xf2\x07\xab\x44\x1c\x45\x8c\x4b\xf0\xd1\x70\xaa\x03\xd6\xa3\xb7\x67\x49\xc3\x52\xb6\xbb\xbc\xb6\xa1\xf2\xfa\x66\x1e\x25\x8a\x5d\x78\x6a\xc6\xfb\x94\x18\x3f\x0a\x46\x07\x39\xb4\xdf\x68\xef\xb0\xb8\xe4\x96\xe6\xe3\x02\x87\xe0\xfe\xa6\x5a\x6b\x9e\x01\xb0\x5f\x54\x58\x4b\xe7\x67\xe7\x1e\xd7\x53\xb2\xd2\x97\xc4\x38\x5f\x96\x9e\x00\x2d\xd3\xd7\x53\x29\x4c\xd1\xb5\x28\x12\x37\x8f\xee\x23\xfb\xcf\x12\xf1\xb5\x79\x44\x3c\x46\x07\xc5\x2d\xd2\x52\x67\xef\x2e\xcf\x93\xed\x1a\x05\xbf\x7a\x6f\x01\x1e\x2e\x9a\x8f\x73\x0d\x92\x95\x1a\x61\x09\x63\xc6\xc9\x67\x70\xdc\x00\xfe\x88\x79\xa9\x16\x1a\x1c\xe1\x21\x09\x48\x59\x39\x5c\x07\xcf\x2c\xe0\xb2\x11\xf2\xd4\x7c\x7f\x51\x62\x6b\x5c\x3b\x66\x59\xd0\xf4\x77\xa4\x0d\x4e\x9a\xa8\xd6\x35\x5e\x1e\xa6\x76\x81\xd7\xbd\xb9\x2c\x02\x10\x2e\xb9\x91\x25\x6c\x99\xc2\x8c\x08\x04\xbe\x5b\x16\x4a\x16\x08\xd9\x46\xef\xc7\x19\x40\x79\xd9\xfa\x13\xac\xe7\xe6\x63\x8b\x1b\xe5\xa2\xd4\x2c\xda\x32\xa5\xa9\xee\x6f\x4c\x2a\x45\x39\x3b\x51\x46\x83\x83\xc7\xf8\xec\xe8\x55\x61\xea\x1d\x42\x5e\xa8\x38\x75\xd4\x9b\xda\x05\x3e\x86\x06\xab\xf0\xa8\x78\xd1\x51\xe1\x9e\xc2\x31\x20\x42\x7d\xf8\x54\xc2\x3e\xc2\x81\x80\xfa\x54\x96\x4b\xbc\x8a\x65\x47\x66\x67\x04\x35\x92\x8d\x56\xbb\xde\xc8\x10\x6d\x95\x47\xcd\x25\xfa\x22\x0e\x87\xc0\x15\x2b\xf5\x7c\x22\x42\x11\x60\x6f\x62\x0f\xfa\x09\x87\x51\xac\x8b\x9a\x0d\xa3\xdd\x36\x03\x49\x3e\xc9\xe6\xf4\xdc\xfe\x93\xa9\xed\x55\x52\x76\x6e\xb6\xeb\x74\x23\x65\x22\x3d\x4e\x24\x70\x82\x5b\x5a\x42\xc4\x94\x4a\xfc\xc9\x2c\x2d\x44\x64\xa2\x86\x88\xb0\x08\x0a\x49\x80\x79\xfa\x1d\x6b\xbb\x09\xa0\x9b\x14\xf1\x0d\xf2\x02\x1c\x0b\xed\xa7\x60\x8a\xae\x7e\x3d\x37\xf1\x8e\xf9\x06\x77\x8a\xeb\x54\xf1\x4d\x33\x3a\xb5\x1d\xba\xbd\xb1\xde\x98\x4e\x67\x68\x73\x6a\x70\x63\x6c\x84\xc8\xf0\xbc\x64\x3c\x65\xdd\x96\x22\x8c\xeb\x0b\x2e\xf4\x57\xbb\x8f\x73\xae\x84\xb0\x3b\x90\x13\x20\x5c\x4f\xfe\x96\xb2\x59\xba\xa7\x11\x0b\x02\xf6\xa0\x3f\x29\xad\x07\xd6\xdf\x98\x75\x72\x73\x73\x23\xee\xb2\x82\x39\xd5\x0e\x61\xe1\xd9\xef\x33\xe0\xeb\xe5\x89\x40\x03\x4c\xfd\x41\xea\x9a\x3d\x86\xa4\xad\xd9\xe7\x8f\x2b\xe9\x33\x5f\x31\xcf\xcd\x30\xdd\x94\x26\xa5\xe9\x83\xbf\x85\x18\x47\xc4\xc0\x68\x89\x43\x44\x20\x08\x23\x39\xdd\x52\xcf\x32\xdf\xd9\x6c\x4c\x89\x38\x50\x11\x01\xcf\xcd\x9f\xa2\xa6\x35\x93\xeb\x28\x60\x3e\xe4\x0e\x2f\x96\x65\xbd\x20\xca\xb6\xb8\xa7\x43\x6b\x54\x68\xa8\x51\xe1\x04\xc1\x63\xb5\x50\xc8\x69\x00\x7d\x9d\x1f\x30\xb6\x42\x7f\xc3\xd0\xad\x61\x99\x82\x69\xa0\x4c\xa1\x2c\x59\x98\xaf\x59\x0b\x34\xea\x61\x02\x1c\x72\xea\x94\x75\x99\xd3\x2a\x74\xa4\xe4\x04\xfc\x44\x3b\x94\x5d\xd2\xe8\x0c\x5d\x6a\x72\x6e\x14\x97\x6e\xb6\xd0\x8d\x35\x04\xf5\x67\x22\x2d\xea\x9f\xda\x39\xbc\xd9\x42\x98\xfa\xe8\x26\xf1\xdd\x6f\x32\x45\x4b\xbb\x30\x35\x88\x8c\x9b\x49\xbf\xf9\x9f\xbf\xab\xb6\xcf\x6f\xb4\xd8\xdc\x9c\x9f\xfd\x72\xea\x68\xe3\x31\xfa\x31\xa6\x9e\x24\xf7\x50\x6c\x7f\x74\x71\x72\x63\xba\x7c\x73\x79\xd3\x42\x3f\xb1\x07\xb8\x07\xbe\x85\xa6\x2c\xd6\x86\x41\x8d\x1c\xa3\x10\x7f\x22\x61\x1c\x2a\x1e\x74\xda\x19\x3a\x46\xf5\x58\x71\x3a\x52\x2d\x16\x16\xfb\x4f\x67\x72\xe6\xd2\xce\x42\x68\x6c\xce\xe8\x28\xbe\x69\x89\xbb\xc1\x0f\xa2\x29\xee\x44\xd3\x64\x99\x0c\x91\xda\xad\x34\xac\x41\x37\x66\x2b\xe5\xa6\xae\xba\xe6\x75\xf5\x39\xca\xe3\xd7\xe8\x53\xd4\xcf\xf3\x7b\x38\xba\xf9\x3f\xa3\xe6\xbf\xdc\xc3\x30\x55\x27\x24\xa9\xac\x30\xc3\xc0\xa6\x17\x73\x80\x40\x62\x2e\x85\x79\xae\x46\xb5\x22\xc5\x01\xb9\x05\x45\xf4\x5f\xba\xbb\x5f\xc4\xb0\x68\x73\xa9\x5e\xe6\xa7\xc5\xb2\x37\x58\xea\xf7\xb1\x00\x8e\x26\x58\xa0\x08\x78\x48\x84\x48\xca\x4e\x04\x80\x16\x29\xc3\x17\xf0\x2d\x39\xb8\x60\x12\x5a\x29\x7d\x66\xd1\xc9\x6a\xfe\x95\xc4\x27\x89\x7b\x22\xac\xd6\xd5\xe6\x2b\x71\x1a\xb4\xcc\x55\x18\x25\xb7\x01\x72\xac\xf1\x39\xfb\x82\x8a\x66\xaf\x96\x94\x34\x56\x33\x6f\x1b\xd9\xd1\x1f\xbd\x9f\x92\x92\x95\x9c\xfd\xb1\x91\x42\x1f\x0d\xf5\xd3\xe4\xa1\xf9\xe3\x65\x92\x44\xfd\xf9\xfd\xf5\x86\xdd\xe3\x44\xca\x48\x61\xcf\x8f\xb6\xd6\x35\x8b\x85\x1a\x16\xc3\xe8\xc6\xeb\x69\xee\x16\x92\x79\x9f\x13\x2f\x20\x20\x7e\x1f\x05\x6c\x3c\x10\x84\xde\x0e\xda\xad\xce\xec\x85\x29\x75\xcb\x61\x9a\xbd\x5b\xaa\x8c\x2e\xf9\x92\xbe\xdd\x49\xa3\x40\xff\x39\x1b\xa3\x2b\x42\x6f\x67\x8f\xd3\x14\x0c\x6a\xe4\xa0\x5d\xf9\x92\x66\xd1\x12\xe4\x83\xf5\x22\xe6\x2c\x9d\xb0\x22\xfd\xad\x88\x8e\x33\x8a\xca\xf9\x82\x26\x12\x76\x7f\x55\xd1\x7a\x53\xef\x9b\x0d\x8a\xfb\x66\x4d\xd7\xbe\x59\x39\x06\xad\xae\x33\x0c\xc3\x72\x5a\x26\x53\xb5\x7f\xfe\xab\x18\x8f\x11\x19\x98\x09\xa8\x1b\x15\x57\x77\xae\x7e\x61\x1c\x48\x32\x08\x08\x75\x9e\x19\x99\x6d\xc0\xdb\x2a\x9f\x07\xb0\xe6\xee\xb5\xc2\x85\xce\x09\x75\x41\x26\x84\xcf\x87\xa9\xb8\xc5\x35\xfd\x7d\x6a\x8e\x39\x8b\xa3\x3e\x6a\x00\xf5\x23\x46\xa8\x2c\x57\x7c\x8a\x09\x7b\x18\xe0\x20\x78\xfc\x70\xae\x26\xec\x41\xad\xf7\xd5\x83\x99\x07\xf1\xc8\xa1\x48\x16\x11\x6f\x41\x9e\x91\x85\xa1\xf2\x13\xd4\xea\x24\xc1\x9f\x15\xb8\x99\xc5\x53\x23\xd0\xea\x2a\xdc\x22\x74\x5d\x0d\x50\x95\x1c\xb2\xc9\xd6\x4a\x97\xa7\x59\x48\x88\x1e\x9f\x40\x28\xa4\xd7\xb3\x5f\x73\xae\x20\x27\x38\xa9\x00\x2e\x07\xda\x69\xac\x82\xa9\x0e\x2b\xcb\xbf\x23\xdf\xd7\x9b\x03\xb1\x90\x2c\x34\xbe\x68\xea\x8d\x78\x4c\xbb\x27\x32\x59\xf9\x13\x7f\x37\x04\x21\x4c\x1e\x00\x49\x8e\xa9\x20\xb2\x98\xfd\xc9\x7e\x8b\x87\xa3\x7e\x0b\xc6\x52\x1a\xcf\x75\xea\xee\x25\x3e\xb7\x21\x5a\x32\x15\x90\x62\xdf\xb7\x8a\x3e\x5c\xbf\x44\x38\x5e\xaa\x46\xf3\x01\xab\x85\xc4\xfe\x95\x4a\x38\x6b\x50\x6f\x18\x6a\x93\x5f\x87\xe4\xdf\x54\xab\xc7\x93\xec\xde\x7a\xc9\xff\x9a\x0b\xa9\x6a\x9a\x41\x54\x42\x24\x34\x9f\x69\x71\x35\xdc\x46\x47\x9e\x2c\x6e\xe4\x94\xa9\x77\x1a\xf8\x7a\x94\x37\x73\xda\xe1\x04\x5a\xd0\x47\x1d\x0d\x84\x4f\x92\x63\x6f\x39\x15\x3c\x35\x6d\x10\x4e\x84\x75\xc4\x99\xb9\x15\x7c\xc8\xfc\xa2\xd5\xc8\x7e\x7f\x7c\xf5\x79\x0a\x59\x4c\x28\x4a\x59\xfc\xb5\x44\x2d\x27\x06\x5f\x4a\xd6\x26\x58\x0c\x26\x80\x7d\xe0\x83\x11\x09\x24\x94\x6a\x06\xb2\x5f\x6e\x8e\x5f\x6a\x60\x34\xc4\x42\x45\xff\x26\xb3\x60\xb6\x82\x3d\x3d\xef\x8c\x02\x32\x78\x1f\x29\x7c\xae\xdd\xcf\x39\x74\x29\xd9\x33\xfd\x26\xb1\x2e\x43\xa0\xec\x48\x56\xd5\xe4\xfe\xa5\x67\x7a\x92\xc6\x17\xc5\x5d\xe2\xe2\x2f\x91\x89\x9f\x4c\x57\x8b\xc1\x9f\x4e\x56\x4b\x1b\xd8\x2e\xb2\xb0\x48\x49\x4b\x26\xea\xcb\x8b\x6b\x49\x92\xea\x89\xec\xfc\x8f\xc7\x3b\x43\xbf\xd7\xd3\x73\x36\xb6\xcb\x77\x16\x54\x7f\x15\x4e\x30\x39\x2e\xaf\xb4\xce\x9b\xa1\xc6\xe1\x50\xdc\xb7\xc5\xbe\xa4\xb0\x3f\x6e\x77\xc7\x93\xdd\x71\xcf\x8a\x7e\x4a\x95\x93\x56\x9b\xbd\x21\x1f\xf1\x76\xbb\x1b\x8d\xe8\xed\xa4\x9d\xef\x20\x3d\xd8\x88\x1a\x82\xdf\x7b\x4d\xec\x79\xb2\xd9\xd9\xeb\xc2\xa8\xeb\x1f\x34\xdb\xdd\xf6\x61\xb3\xd7\xe9\xec\x37\x0f\x7a\x7b\xdd\xa6\x3f\xda\xdb\xf1\xba\xed\xee\xae\xd7\xdd\x73\x60\x49\x0e\x3d\xa2\xc6\xb0\xd3\xeb\xf9\x87\x87\x9d\x66\xfb\x00\x86\xcd\x5e\x6f\xbf\xdb\x3c\x00\xaf\xd3\x84\x61\x7b\xa7\xe7\xed\x1d\x76\x77\x3a\x43\xbb\x7d\xcc\x83\x3e\x6a\x8c\x18\x6b\xba\xe8\x6d\xdd\x62\xd1\xc2\x5e\x08\x2d\x8f\x85\xfd\x5e\x6f\xa7\x51\x88\xc6\x9c\x15\x99\xd6\xf0\xdb\xb7\x07\x01\x1d\xb7\x77\x3a\x02\x0e\xef\x6a\x0c\x1f\xda\xdd\xdd\xee\xde\x2e\x34\xf1\xc1\x01\x6e\xf6\x7a\xa3\x61\xf3\xa0\xb7\xdb\x6e\x82\xdf\xee\xb4\x61\xb8\x37\xf4\x76\xbd\x79\xc3\xf7\xbd\x5d\x7c\xd0\x3d\x3c\x68\x0e\xc1\xdf\x6f\xf6\xba\x5d\x68\x1e\x1c\xf6\xf6\x9b\xa3\xbd\x91\x8f\xf7\x0e\xbb\x87\xdd\xd1\xa8\x3c\xfc\x21\xe6\xc9\xf0\xbb\xe1\xc8\xc3\xed\x76\x57\x1e\xde\xed\x8b\x71\x4b\xf0\xaa\xe1\xa7\xd5\x89\xc5\xb0\xbb\x5c\xe7\x88\x1a\xee\x98\xdf\x59\x71\xea\x8a\x5c\x67\xb1\x97\x9d\x5a\x32\xbf\x2c\xce\x14\xa5\xb7\x49\xac\xa3\x27\x77\x6b\x88\xf3\xd7\x58\xcd\x82\xee\x7c\x57\xb7\x30\x2d\x6a\x73\x5a\x01\xd7\xb8\xba\xbe\x3c\xbb\x78\x95\x8f\x4d\x9c\x7e\xe8\xac\xc5\xcf\x57\x6f\x2e\x0a\xc7\x02\x93\x98\xbe\x58\x81\x33\x3f\xc0\x48\xb2\x3b\xfa\xad\x32\xab\xe5\xf0\x34\xcd\x85\x69\x10\xed\xb2\x56\x15\x6c\x16\x2a\xbd\x75\x3a\x6f\x90\xd6\xe1\xda\x5d\xfb\x80\xfd\x41\x00\x52\xd9\x80\xbb\x18\x8a\xc3\xd4\xdc\x55\x02\x17\xdc\x99\xae\xaa\xbf\x12\xe1\x48\x35\x35\x3a\x6d\x4b\x96\x12\x63\x54\xb8\x97\x62\x7e\x76\xc6\x7c\x1b\x62\x3b\x87\x47\xdf\x28\x80\x1a\xc7\x6f\x2e\x2e\x4e\x8f\xaf\xdf\x5c\x36\x5f\xbf\x7a\x7d\xdd\xcc\x81\x24\xf7\x08\xa0\xc6\x95\xf5\x2d\x98\xf4\x2b\x31\x02\x51\x26\xb3\xf2\x08\x93\xfd\xd5\x5f\x8d\x79\xae\x64\xab\x7c\x26\xad\x70\xd1\x00\x6a\x74\xc8\xfb\x33\x12\xde\xbd\xf2\xf8\x49\x7c\xbe\xd7\xc1\xef\x3e\x9d\xfd\xe3\xee\xc5\xf5\xdd\xc5\x25\x9e\x71\xe9\xcc\x64\x53\x7f\x8d\x81\x4f\x6b\x70\xaa\xfb\x44\x9c\xea\x2e\x64\x54\xd7\xc1\xa7\xff\x58\x93\xfe\x52\x9f\x00\x30\x5f\xbe\xe3\x02\x72\x7b\x09\x7d\xf4\x8e\x2a\x3b\xa0\xde\xea\x8c\xc1\x2f\xf6\x27\x16\x85\x3e\xa0\x85\x23\x32\x30\x49\xb5\xa4\x38\xbe\x8f\x4a\x14\xf4\x97\xe8\x2f\xab\x63\xf1\x58\x10\x87\xd4\x78\x37\xaa\xa7\x24\x59\x8c\x36\x89\xbf\xd9\x42\x57\x2e\x38\xbd\xab\x62\xf7\xa6\x0c\x39\xa3\x5b\xc9\x5e\xa7\x17\xb0\xd8\x1f\x24\x19\x79\x9e\x3e\x35\xf5\xac\x2d\xf4\xab\xc9\x8c\x9b\x89\xec\x23\xe2\xa3\xe7\xa8\xd3\xdd\xa9\x94\x8a\xe0\xfd\xc9\xab\x78\x3a\x3c\xe3\xa7\xf4\x13\x3f\x82\x70\xbf\xdb\x1b\xdf\xdd\xde\x92\x93\xfb\x54\x2a\x8a\x77\xd3\xb8\x24\xa1\xd7\xee\x3d\x89\x24\xec\x2f\x12\x84\x7d\x87\xbe\xd4\xf9\x8e\xc6\x6c\x30\xce\x7b\xcf\x5c\x43\xda\xff\x76\x03\x3a\xce\xdd\x63\x8b\x88\xff\x7c\xb3\x43\x7e\xd9\xf1\xe3\xdf\x3e\x9c\xdd\xdf\xef\x7e\xb8\x3f\x0f\xa6\x9f\x3b\xe1\xab\xcb\x9d\x9f\xa7\x77\x17\x9b\xda\x34\x8c\x58\x4c\xfd\x39\xca\xff\xe1\xcd\xfe\xb8\x3b\xde\xfb\xe9\xda\x7f\xf7\xcb\x3b\xdc\xbd\x15\x3f\x1d\x74\x6f\x7f\x3d\xd9\x99\xa6\x9c\x29\xde\xd3\xe4\x34\x8d\x9d\xa7\xb1\x8c\x9d\x85\x86\xb1\xe3\x60\x4b\xa6\xc6\xf7\xc0\xc9\x68\x8a\x7e\x7e\x7f\x6d\x6e\x7f\xea\xa3\xcb\xc4\xe1\x45\x38\x96\x13\xc6\xc9\xe7\xf4\x24\xf3\x2d\xd0\x7a\xfc\xd9\x79\x37\x39\x9d\x3c\x84\xbf\xbf\x88\xde\xbf\x1d\x9d\x75\x83\x0b\xb8\x8d\xfc\xde\x3f\x4e\x52\xfe\x1c\xaa\xe5\xed\x98\xd1\x51\x40\x3c\x59\x83\x57\x3b\x7b\x4f\xc2\x2b\x1b\x8d\x9b\x57\x36\x84\x2d\x42\xa6\x6e\xce\x58\x1e\x22\x10\x0e\xf4\xf2\xaa\xcb\xbb\x2a\xf9\xb0\x77\xfb\xa1\xfd\x8e\x9c\xde\x7e\xbe\xfd\xfd\xf8\xf3\xfb\xb7\x70\xd6\x65\x1f\x60\xe2\xef\x9c\x26\x6c\x28\x5f\xb8\xe4\x1a\xfa\xe1\x93\x8c\xfc\x70\xd1\xc0\x0f\x9d\x32\x92\x5d\xd0\x08\xf9\x4e\x4b\x53\x0e\xa7\xe7\xf7\x2f\x0f\x3f\xbe\xfe\xf5\xc3\xde\x87\xf1\x64\xf4\xfa\x70\xfc\xea\x52\xfc\x74\x7f\xfa\x7e\x36\xd6\xda\xc6\xe2\xdb\x8d\xd8\x5e\x05\x75\x9f\xb3\xc3\x6f\x48\x79\x07\x42\xb9\xde\x6f\x8e\x5f\x37\x4f\x7f\x6f\x1e\xf6\x93\x93\x72\x4a\x85\xcc\x79\xb8\x0c\x06\x3e\xc9\x66\xb2\xf6\xe1\x88\x34\x3b\xe4\x53\x7b\x27\xa0\x7e\x10\xde\xb5\xef\x46\xde\xbe\x20\x12\xef\x8a\xe0\xe3\xfd\x81\xed\xc7\x8e\xac\x8b\xc4\x14\x1f\x3a\xe3\x5d\xff\xe0\xe0\xae\x1d\x70\xcf\xbf\xef\x8d\xf7\x71\x30\xdc\x17\xc1\x68\x4c\x3f\xee\xf8\x93\xa1\xf8\xf8\x97\xff\xf7\xd7\xd3\xdf\xaf\x2f\x8f\xd0\x7f\x99\x11\xb7\x34\xc5\xcf\x89\x0f\x54\xaa\x39\xb3\x83\x50\x22\xd0\x66\xaf\xdd\xdb\xdc\xd2\xbc\xd0\x7f\x1e\x9f\xbf\xbb\xba\x3e\xbd\xbc\x32\xcc\x50\x2f\xf5\x5e\xea\x6c\x62\x51\x86\x48\xc3\x77\xc6\xbb\x8c\xef\xb6\xef\x49\xdc\xde\x67\xa0\xa6\x6d\xc2\x6f\xbd\xee\x9e\x3f\x1e\xc9\x8f\x1d\xec\x6d\xda\x8b\x6c\x7a\xb7\xf6\xe6\xa2\x41\x58\xf6\xf6\x6f\x73\xec\xc9\xb5\x78\xcf\xa7\x7b\x54\xdc\x0d\xbb\xe2\x22\x7c\xf9\x71\x77\xf8\x7b\x74\xb2\x7f\x8c\x1b\x1b\xff\x17\x00\x00\xff\xff\x48\xdc\x50\x6f\x24\xa1\x00\x00") +var _connector_mgmtYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x6d\x73\xdb\x38\x92\xf0\x77\xff\x0a\x3c\xca\xb3\xe5\xdd\x3b\x4b\x96\x64\xf9\x4d\x75\xd9\x2a\xc7\x76\x32\x9e\x71\x9c\x8c\xed\x4c\x26\xbb\xb5\x25\x43\x24\x24\x21\x26\x01\x1a\x80\x64\x2b\xbb\xf7\xdf\xaf\x00\x90\x22\x40\x82\x14\x25\x2b\xb6\x33\x23\x56\xed\x4e\x4c\x02\x8d\xee\x46\xa3\xd1\x6f\x80\x68\x84\x08\x8c\x70\x17\xec\x34\x9a\x8d\x26\x78\x05\x08\x42\x3e\x10\x23\xcc\x01\xe4\x60\x80\x19\x17\x20\xc0\x04\x01\x41\x01\x0c\x02\x7a\x0f\x38\x0d\x11\x38\x3b\x39\xe5\xf2\xd5\x2d\xa1\xf7\xba\xb5\xec\x40\x40\x0c\x0e\xf8\xd4\x1b\x87\x88\x88\xc6\xc6\x2b\x70\x14\x04\x00\x11\x3f\xa2\x98\x08\x0e\x7c\x34\xc0\x04\xf9\x60\x84\x18\x02\xf7\x38\x08\x40\x1f\x01\x1f\x73\x8f\x4e\x10\x83\xfd\x00\x81\xfe\x54\x8e\x04\xc6\x1c\x31\xde\x00\x67\x03\x20\x54\x5b\x39\x40\x8c\x1d\x05\xb7\x08\x45\x1a\x93\x19\xe4\x8d\x57\xa0\x16\x31\x3c\x81\x02\xd5\xb6\x00\xf4\x25\x15\x28\x94\x8d\xc5\x08\x81\x9a\x47\x09\x41\x9e\xa0\xac\x17\x0e\x43\x51\x8f\x5b\x36\xa6\x30\x0c\x6a\x60\x80\x03\xb4\x81\xc9\x80\x76\x37\x00\x10\x58\x04\xa8\x0b\x8e\x93\x0e\xe0\x0a\xb1\x09\xf6\x10\x78\x1b\x20\x24\xc0\x7b\x48\xe0\x10\xb1\x0d\x00\x26\x88\x71\x4c\x49\x17\x34\x1b\xad\x46\x73\x03\x00\x1f\x71\x8f\xe1\x48\xa8\x97\x73\xfa\x6b\x7a\x2e\x11\x17\xe0\xe8\xe3\x99\x44\x33\x54\x1f\xc0\x0c\x51\xde\xd8\xe0\x88\xc9\x41\x24\x56\x75\x30\x66\x41\x17\x8c\x84\x88\x78\x77\x7b\x1b\x46\xb8\x21\x99\xcd\x47\x78\x20\x1a\x1e\x0d\x37\x00\xc8\x20\xf0\x1e\x62\x02\xfe\x1a\x31\xea\x8f\x3d\xf9\xe6\x6f\x40\x83\x73\x03\xe3\x02\x0e\xd1\x3c\x90\x57\x02\x0e\x31\x19\x3a\x01\x75\xb7\xb7\x03\xea\xc1\x60\x44\xb9\xe8\x1e\x34\x9b\xcd\x7c\xf7\xd9\xf7\xb4\xe7\x76\xbe\x95\x37\x66\x0c\x11\x01\x7c\x1a\x42\x4c\x36\x04\x1c\xc6\x0c\x20\x30\xb4\xe6\xe5\x7a\x1a\x21\x9e\xef\x5f\xab\xb9\x5a\x57\x6e\x08\x8e\x83\x31\x17\x68\x81\x0e\xf1\xfc\x3a\xdb\x6f\x44\x50\x8c\x14\xfe\xaf\xe4\xff\x80\xb3\xdb\xab\x8d\x0d\x00\x6a\x72\x1a\xb6\x6d\x31\xdd\x9e\xb4\x6a\x5d\x05\x77\x88\x84\xfe\x07\x00\x09\x43\xf4\x53\x2f\x40\x04\xc8\xb5\xc8\xa0\x44\xe4\xcc\xef\xca\xfe\xbf\x69\x71\x7d\x8f\x04\xf4\xa1\x80\x71\x2b\x3e\x0e\x43\xc8\xa6\x5d\x70\x89\xc4\x98\x11\xae\x56\x4b\x2c\xd9\x20\xb4\xdb\x5a\xc4\x55\x68\xcf\x10\x8f\x28\xe1\xc8\x40\xb7\xd6\x6e\x36\x6b\xe9\x9f\x40\x8a\xbb\x40\x44\x98\xaf\x00\x80\x51\x14\x60\x4f\x21\xbf\xfd\x95\x53\x62\x7f\x05\x80\x7b\x23\x14\xc2\xec\x5b\x00\xfe\x3f\x43\x83\x2e\xd8\x7c\xb5\xed\xd1\x30\xa2\x04\x11\xc1\xb7\x75\x5b\xbe\x9d\x21\x7f\xd3\xe8\x6c\xd1\xf5\x5b\x96\x96\xd9\xdc\xe5\x25\xaf\x6c\xe2\xb6\x6f\xe1\xe0\x16\xf6\xd2\xf7\x42\x76\xda\xfe\xb7\xfd\xa2\x87\xfd\xff\x8d\xf9\x11\x41\x06\x43\x24\xe2\xf5\xae\xe7\x56\x8b\x5a\xae\xcb\x86\x13\xf3\xeb\x11\x02\xd8\x07\x54\x69\xcc\xb4\x13\x90\x9d\x36\x8a\x59\x27\x3f\x77\x01\x17\x0c\x93\xe1\xec\x35\x26\x5d\x20\x45\x77\xf6\x82\xa1\xbb\x31\x66\xc8\xef\x02\xc1\xc6\xa8\xba\x4c\xa6\x8b\x14\x00\x8e\xbc\x31\xc3\x62\x6a\xb6\x7c\x83\x20\x43\xac\x0b\xfe\x09\xfe\x55\x20\xb7\x33\x58\x12\xd4\x9b\xe9\xd9\x49\x56\x72\xdf\x21\x01\x60\x86\x5e\xb9\x8b\xcc\xf8\x64\x71\x69\x6e\xeb\x67\x92\xda\x9a\x53\x6a\x2d\xe2\x6b\x99\xae\xe8\x01\x86\x51\x60\x22\x9a\x3c\x56\xb7\x53\xdd\x2c\xdf\xca\x3d\x74\x02\x75\xdb\x05\xa4\x56\xb4\x6c\xae\x73\x22\x07\x42\x28\xbc\x91\xdc\x2e\xa4\x38\x4a\xf9\x41\x4a\xf3\xc7\x2c\xed\x34\x5b\xcf\xc3\xd2\x53\xc6\x28\xab\xce\xca\x4e\xb3\xb5\x2c\x03\xd3\xae\x85\x6c\x3b\x1a\x8b\x11\x10\xf4\x16\x11\x69\x10\x60\x32\x81\x81\xb1\xbc\x6b\x9d\x66\xe7\x07\x61\x52\x67\x79\x26\x75\xe6\x31\xe9\x82\xa6\xb2\x94\x91\x31\xf4\x80\xb9\xe0\x29\xc3\x76\x9f\x6b\xa1\x2e\xc8\xb0\xdd\x66\x73\x59\x86\xa5\x5d\x0b\x19\xf6\x89\xa0\x87\x08\x79\x02\xf9\x00\x49\xbc\x00\xf5\x94\x55\xe5\x2f\xbc\x5f\x2d\x62\x7e\xac\x58\xd5\xf3\x22\x0b\x05\x82\x00\x73\x21\xf7\x39\x5b\x18\x78\x99\x99\x32\xaf\x53\x7e\xf7\x95\x28\xbb\x26\x22\x6d\xb9\x1d\xc1\xa1\x31\x09\x73\x9b\x73\xfc\x6d\x91\xe6\x94\xf9\x88\xbd\x99\x2e\x32\x00\x82\xcc\x1b\xd5\x5e\xfc\x46\x76\x8e\xb9\x28\x56\x89\x73\x66\x6a\xbd\x77\x54\xdb\x3b\xd6\xaa\x70\xae\x2a\xcc\xd8\xf5\x0b\x5a\xf4\x89\x72\x8c\xa4\xc7\x3b\x4f\x3b\x3e\x42\x31\x7a\x0c\x41\x81\x4c\x2c\x81\xa9\x16\x8f\xd5\x67\x15\x1c\xb9\x4f\x97\x8c\x4b\x17\x96\xb6\x74\x2b\x40\xe9\x07\xdc\x8d\x11\x9b\x1a\xfc\xd5\x4e\x09\xe4\x53\xe2\x15\x71\xfd\x23\x62\x03\xca\x42\x65\xf9\x41\x15\x7d\x00\x98\x00\x48\x74\xaf\x11\xa3\x84\x8e\x39\x08\x21\x21\x88\x6d\x94\x4b\x9b\x76\x4f\xfa\x94\x06\x08\x12\xe3\x8b\xc3\x21\x01\x89\x95\xf9\x86\xfa\x06\x83\x0b\xc2\x32\x86\xa3\xea\x5c\x1c\xe5\x4b\xc3\xbd\x30\x2a\x69\xc0\x4b\x8d\xa4\xbd\x42\x8a\xd6\xc7\xac\x97\x9e\xbc\xc2\x95\x52\xcd\x92\xb7\x80\xd4\xca\x9c\xbb\xa2\xed\xa3\xfd\xcc\xdb\x47\xb1\x36\xf4\x3c\x14\x09\x64\x19\xcf\x3f\x86\x02\xec\x34\x9b\x6a\x5e\x30\x25\xcb\xef\x16\x59\x10\x85\x7c\xfa\x4d\xee\x12\xaa\xa5\x56\x88\x3c\xd5\x88\x06\xe7\xd6\xfb\xeb\xda\x37\xab\xe4\x9b\x5d\xa7\xbe\x3d\xf2\xa5\xce\xa0\x63\xe6\x21\xe0\x53\xc4\xc9\xa6\xd0\xfe\xd9\xda\x26\xc9\x08\x16\x01\xe3\x22\xb3\x44\xef\xf6\x49\xd4\xc4\xde\xa4\xab\x78\x61\x8f\xb0\x33\xa4\xd9\x9d\x87\xf3\x07\xf3\xbe\x5e\xb4\x6f\xb4\xa8\x5f\xb4\x76\x89\xd6\x2e\xd1\xf3\x44\x87\xf8\xf6\xbf\xcb\x33\x17\x73\x16\x23\xf6\x6b\x4f\xa1\xd2\xcc\x98\x52\x56\xa1\x65\x12\x01\x2e\xf5\xe5\x6e\xf2\x32\x75\x47\xc5\xc8\xfc\x3a\x28\xbf\x36\xfc\xaa\x30\x69\x1d\x94\x5f\x88\x61\x8f\x52\xbb\xba\x69\x80\x04\xfa\x9e\xba\x50\x8f\x50\xa8\x0e\x4f\xd4\xe7\x79\x1a\xb1\xb0\x95\x5b\x29\xbe\x94\x85\xe2\xa0\x61\xed\xee\xfe\x61\xb5\x9e\x9e\xe0\x47\xe8\x3e\x0b\x40\x99\x06\x54\x56\x51\xb2\x8d\x82\x7b\x2c\x46\x80\x47\xc8\xc3\x03\x8c\x7c\x70\x76\xf2\x23\x6b\xc2\xc7\x31\x31\x0b\x60\x49\xad\x18\xc9\x1d\xe6\x7b\x2a\x45\x35\x40\xa1\x4e\xfc\x28\xbf\xce\x53\x89\x45\x8d\xe6\xc7\xa2\x4f\xa0\x80\x40\x50\x8d\x44\xa6\x68\x47\xca\xd2\x46\x89\x98\x98\x42\x12\x22\x36\x44\x75\x05\xe5\xbf\xab\x46\xaa\x75\x58\x9d\xf6\xbf\x22\x4f\x14\x80\x95\xa0\x16\x84\x9a\x71\x58\x7f\xbe\xfa\x70\xa1\xf9\xb3\x05\x2e\xdf\x1e\x83\xbd\xc3\x66\x1b\xd4\x67\x75\x87\x82\xd2\x80\x37\x30\x12\x83\x06\x65\xc3\xed\x91\x08\x83\x6d\x36\xf0\x64\xab\xe5\xb0\x5d\x7d\x88\x7e\xd6\xf9\x8f\x10\x22\x5f\x7b\x02\x7f\xde\x3d\x71\xed\x09\xfc\x08\x9e\x80\xa3\xd6\x34\x2e\x47\x5e\xb4\xda\xd4\x8b\xab\x98\x17\xc9\x51\xdb\xa5\xcf\xe5\x59\xe8\x14\x2d\x50\x79\xe7\x9d\x93\xb2\x06\x9e\x05\xb3\x42\xea\x3a\xd3\xe3\x4f\x97\xc2\x8e\xc9\x7f\xbe\x54\x76\x2c\x05\x4b\x66\xb4\x75\xe7\xd5\x24\xb6\x1d\xb0\x7e\xc8\xfc\x76\x4c\xc8\x3a\xcd\xbd\x4e\x73\x1b\x9c\x5b\xdb\x38\x15\x98\xb4\x4e\x73\xbf\x2c\x33\x67\x89\x34\xb7\xb5\xa1\x57\x2a\x3a\xce\x98\x2c\x8f\x4d\x7b\x67\xc1\x55\xc9\x7e\x7b\x76\x9f\xca\x09\xf0\x4c\xbf\xa7\xae\x40\x7e\x99\x69\xac\x78\x02\x16\xae\x10\xce\x30\x73\xad\xdd\xd7\x19\xf1\x27\x3e\x2f\x91\x48\xa0\x79\xc4\x2f\x7e\xb7\xe0\x29\xbf\xb4\x97\xdb\x01\x28\x3a\xe8\x67\x7b\x43\x4f\x7f\xd6\xef\xf1\xba\xd8\xcc\xd7\x67\x3c\xcc\xa2\xd3\x7e\x25\x4e\x63\x79\xd3\x17\xad\xff\x2a\xc6\xf0\x12\x07\x70\x1d\xcb\x5b\xdb\xb9\x55\x98\xb4\x64\x2c\x2f\x11\xb3\x75\x4c\x6f\xd9\x3c\xd6\xf8\x49\xd4\xe7\x38\xf2\x1d\x31\xba\x37\xd3\x33\x3f\xab\x45\xc7\x7e\x04\xed\x3c\x7e\x99\x22\x9d\xdb\xba\x7a\xae\x4b\xa3\xe8\x2f\x99\xe9\x7a\x92\xe0\xd5\x02\xd1\x22\x5b\x65\xd8\x51\xba\x78\xcd\x70\x01\xc5\x58\xdd\x8f\x12\x93\xbe\xd6\xcb\x6b\xbd\xbc\x62\xbd\xbc\x56\xc9\x2e\x9e\xad\xa4\xe0\x6a\x05\x5a\x39\x53\x78\x55\x60\xd7\xe6\x2b\xab\xca\x34\xf2\xdc\xd6\xeb\x7a\xac\xb5\x5e\x7c\x21\x4c\x7a\xc2\x7a\xac\x59\x60\x76\x5d\x8a\xb5\xca\x52\xac\xd5\x45\x41\xb6\xa1\xef\x53\xd2\x4b\xa3\x20\xeb\xb0\xc8\x72\x61\x91\x23\xc9\xc7\x8f\x33\xae\x65\x77\x93\x82\xd0\xc7\x26\x07\x6a\x02\x0c\x7e\xbb\x76\x97\x85\x7b\xbf\xa8\x58\x8a\xcd\x9a\xd2\x48\xb2\x14\x99\x94\x18\x20\x46\x50\x00\x3e\xa2\xe3\xc0\x07\x7d\x04\xc6\x5c\xdf\x36\xe8\x51\x32\xc0\xc3\x31\x43\x4a\xb0\xf4\x3d\x7d\xa6\x07\xa3\x99\x42\x89\x96\x3b\xcd\xab\xc6\x7a\x3b\xfb\xa3\x6e\x67\xeb\xf0\xcb\x8f\x64\xeb\x3b\x4a\xaa\x2e\x60\x88\x78\x04\xbd\xc5\xef\xf0\x23\xb3\x9e\x0b\xd5\x55\x59\x03\x82\x39\x95\x55\xb3\xc6\x0b\xed\x17\xf3\x6a\xab\x48\x06\x6a\x95\xea\xaa\x6c\x9f\x45\x4a\x93\x66\x7d\x9f\xaf\x38\x69\xc6\xc8\xe5\xca\x93\x66\xdd\x57\x52\xa0\xe4\x86\xf6\x43\x96\x28\xcd\x48\x59\x17\x29\xad\x8b\x94\x0c\xce\xad\xad\x87\x0a\x4c\x5a\x17\x29\xbd\x2c\xc3\x61\x99\x22\x25\x7b\x5f\xac\xe4\x03\xe6\x2c\x80\xc7\x16\x2a\xe5\x01\x56\x29\x55\x22\xd9\x5e\x95\x8b\x95\x72\x3d\xd7\x17\x26\xa6\xcf\xd3\x6c\xb7\x0b\xd7\x44\xe5\xe6\x6c\xbd\x9d\xac\xab\xa2\x9e\xb8\x2a\x2a\x95\x41\x33\x22\x38\x7b\xbb\x60\x65\x94\xd9\xcf\xed\x81\x14\x05\x01\xb3\xbe\xcc\xd3\x87\x01\x57\xb1\x05\x98\x81\xc0\x9c\x9f\x58\x14\xfb\x2b\x75\xfd\xe6\x35\x7e\xe1\x3a\xb1\x62\x9d\x54\xea\x8d\xae\x2b\xa5\xd6\xc6\x76\x15\x26\x2d\x19\xaa\x4b\x05\x6d\x1d\xac\xfb\x9e\xb5\x52\xab\x50\xa6\x99\x6a\xa9\x19\xc8\x8a\xf5\x52\xa5\x6a\xb5\x42\xfb\x1f\xb2\x66\xaa\x38\xa6\xb6\x9a\xaa\xa9\x19\xfc\x75\xdd\xd4\x5a\x4b\x3f\x81\x96\x5e\x2b\x68\x17\xd7\x56\x53\x39\xb5\x0a\x1d\x9d\xa9\x9d\x2a\xb4\x79\x1d\xf5\x50\xa5\xfa\xb9\x42\xfb\x75\x05\xd5\x5a\x43\xbe\x10\x26\xad\x2b\xa8\xfe\x44\x15\x54\x46\xc4\x04\x4d\x60\xb0\xf2\x44\xf3\xe9\x04\x06\x63\xf5\x6e\x95\x99\x66\x3e\xa2\x4c\x80\x00\x4f\x24\xed\xb3\x11\x96\x4a\x40\x57\xea\xfe\xa3\xe6\xa2\x25\xf7\x1f\x99\x8f\x96\x20\x56\x9b\x93\xce\x41\x5c\xe7\xa5\xbf\x37\xd6\xeb\xbc\xf4\x93\x71\x6e\x6d\x62\x54\x60\xd2\x3a\x2f\xfd\xb2\x5c\xb0\xc7\xe5\xa5\x37\xd2\x51\x25\x72\x31\x85\x5d\x7d\x9d\xe1\x2b\xfd\xff\xe0\x98\x86\x21\x25\xf1\x2b\xf5\x9f\x73\x9c\x1a\x19\x33\xc5\x6f\x18\x03\xb7\x98\xf8\xc6\x9f\x11\x1c\x22\xe3\x4f\x8e\xbf\x99\x7f\x0a\x2a\x60\x60\xfc\x8d\x05\x0a\x13\xb3\xc4\x71\x9f\x63\xc4\xa4\xad\x22\xb0\xc9\x6a\x39\xde\xdc\x04\x8d\xc4\x22\xdf\x08\x13\x81\x86\x66\xd1\x37\xfe\x56\xa1\x95\xc2\xb9\xb8\x99\xfa\xa0\xc4\x24\x69\x03\x83\xe0\xc3\xc0\x64\x51\x99\x80\x7d\x50\xf4\x5e\xa2\x01\x62\x88\x78\x56\x66\xbb\xe0\x82\x4b\x17\x53\x80\x5a\x13\x7e\x4e\xea\x9c\xcc\x01\x6a\x26\xa1\x63\x85\x14\x36\x9f\x99\x8c\x3d\xec\x97\x76\x52\xdf\x32\x34\x75\x17\x9b\x60\x3c\x7f\x7a\x2b\xc9\xc0\x48\x72\xbd\xa8\x91\x81\xe7\x7b\x24\xe0\x82\x28\xd2\x7b\x82\xd8\x5c\x04\xb4\x69\xed\xf7\xa0\xa5\xa7\x06\x94\x85\x50\x74\xa5\xd9\x89\xea\x02\x87\x68\x1e\x98\x90\xfa\xca\xdd\x5a\x16\x8e\x7a\x1f\xff\x0c\x78\x6c\x17\x61\x4a\xae\x90\x90\xda\x82\x97\x2d\x6d\x6c\x2e\xec\x31\x0b\x1e\x37\x69\x63\xe6\x58\x45\x0e\x1c\x8f\x3c\x8f\x8e\x49\xa9\xce\xf1\x02\x8c\x88\xe8\x59\xf8\xc5\xef\x38\xf2\x18\x2a\x9b\xbb\x59\xdf\xf9\xf3\x67\x42\x2c\x47\xfd\x04\x45\x01\x9d\x86\x88\x88\x73\xaa\x77\xa0\xa4\xbd\x8f\xa5\x02\x0f\x31\x81\x82\x1a\x22\x13\x63\x36\xbd\x50\xc9\x6c\x4b\x87\x86\x30\x8a\x30\x19\x9a\x03\x66\x0f\x7a\x54\x3d\xc6\x7c\x0d\xd9\x10\x59\x3e\x45\xde\x30\xae\x6e\x42\xdb\xd0\x28\x41\xd5\xb5\xdc\x3c\xc4\x2a\xf6\xce\xa1\xb2\xe1\xe2\x8e\xfe\x38\x9b\x00\xeb\xd7\xf7\xf5\x37\x0e\xee\x29\xbb\x0d\x28\xf4\x39\x10\x54\x5d\xe0\xa8\x8e\x6b\x78\xf6\x45\x1b\x0e\x6d\x30\x67\x07\x5c\x7a\xc3\x4a\xcf\x31\x95\x0b\x5a\x11\x2b\x1e\x41\x6d\x6a\x22\x3c\x03\xdd\x66\xf1\x46\x39\xe5\x99\xdf\xea\x7f\xa2\xcd\x16\xb9\xac\x39\x45\x19\xa8\x1d\x7d\x3c\x8b\x91\xb2\x0d\x44\x2c\x3f\x4e\x5a\xf6\xcb\x91\x46\xcb\x1d\xef\xa9\x65\x36\xf2\x20\xd0\x4a\x3a\x67\x61\xd6\x35\x70\x15\x1e\xe2\x59\xb3\x74\xce\x20\xf9\x9f\x2a\xcd\xf5\x8f\x09\x2b\xfc\xed\xa9\x62\xd3\xa3\x10\x63\xcd\x57\xc8\x18\x9c\x66\xbe\x28\xdb\x2f\x6f\x26\x67\x26\xd4\xa4\x7d\xa1\xa9\xb5\xcc\xda\x78\x6b\xe1\xa6\x61\xfb\x8b\x64\x47\xf1\x86\x68\xad\xa4\x9f\x68\x20\x17\x8f\xb6\xac\xd5\xb9\x2f\xed\x09\xeb\x83\x60\x12\x82\x5a\x59\x1a\x26\x38\x23\x5c\x40\xe2\xa1\xc6\x32\x32\x5a\xb8\x53\xa7\x13\xf1\x2a\xfe\x8d\x81\x38\x12\xeb\x19\xf3\x92\xb6\x29\x10\xe9\x57\xf6\x2c\xea\x8d\x57\x0d\x7d\x89\x86\x98\x0b\x36\x5d\x31\x4b\x14\x70\x90\x00\x7f\x02\xde\xe8\xc6\x80\x25\x23\xae\x8a\x4b\x89\x2c\xa9\xa3\x84\x96\x24\xd9\x87\x0b\xdd\xaa\xf8\x28\x7b\x4c\xb2\x44\xd9\x2e\x69\x60\x4d\x60\x30\x76\xf8\x33\xa6\x12\xcd\x1f\x83\x2c\xc2\x36\xa9\x1c\xcd\x1e\xee\xb4\xd1\x36\xd7\x75\x66\x3d\x57\x3f\x8d\x59\xcb\xba\xa0\xf9\x6b\xae\x67\xac\xce\xee\xf5\x57\x02\x8a\x8c\x83\x61\x71\x05\x91\x71\x68\x4a\x97\x34\xc7\x34\x08\x64\x1a\x8f\x0c\x41\x7f\xea\x1e\x21\x0e\xcc\x9a\x5e\x82\x6b\x7e\x54\x55\x62\xb5\xad\xdb\x06\xec\x9e\x00\xbd\x24\xa5\x91\x6f\x16\xa5\xa5\x85\x1f\x00\xaa\xb8\x35\x88\x02\x48\x90\x71\x16\x57\x57\x48\xd4\x96\x59\x5c\x25\x84\x17\x18\x5a\x26\x4f\x96\xd8\x87\x35\xe4\xef\x85\xdc\x95\xe2\x44\xd9\x94\x71\xab\x45\xc1\x5a\x2c\xea\x9c\x00\xc8\xb9\xdc\x8b\x50\xa1\xa4\x37\x13\xf2\x37\x23\x09\xd5\x85\x69\xd5\xe6\xd0\x22\x54\x3c\x66\x1e\xaf\x62\x79\x75\x12\x65\xea\xa7\x85\x08\xb3\xed\x96\x85\x23\x29\x4e\xcb\x64\x61\x43\x66\xb1\xab\xfd\xdc\x2a\xd0\x78\x7b\x3c\x82\x84\xa0\xa0\x44\xd7\xf9\x68\x00\xc7\x81\x90\x6f\x61\x3f\x40\x05\x1a\x30\xfe\x68\x33\xfc\x04\x71\x69\xdd\x2f\xaa\x4d\xb5\xda\x34\x61\xd3\x28\xb2\x14\xab\x1f\x57\x21\xd8\xc3\x2d\x3a\x0e\xe4\x1c\x0f\x89\xb9\xd7\x25\xef\xac\xc1\x94\x6a\xb4\x5b\xcd\xc7\x70\x00\x71\x90\x47\xd9\x86\xe2\x67\x6a\x29\xea\x52\x74\x26\x58\x9a\xfe\xd9\x86\xd6\x87\x8c\x54\x9b\x76\x52\x69\x48\x55\x5a\x77\x26\xd2\xda\xec\xe9\x41\x1d\x19\x31\xbe\xe4\x7e\xb0\xdf\xe5\x87\x49\x68\xa6\x78\x96\x09\x66\x81\x51\x9c\x2e\xa6\x0c\x2e\x79\xb8\x9b\x65\x96\x5b\x1c\xdb\xd9\x4c\xc1\xa9\xef\xbd\xc4\x58\xab\x8a\xe6\x3c\x8b\x35\xc5\x77\xc6\x21\x13\xf4\x2b\x23\x40\x5e\x66\x1e\xca\x96\x6a\x9b\xe5\x23\x18\x21\xeb\x75\xc4\xa8\x87\x38\x37\x7f\x6f\x57\xbe\xd6\x41\xf9\x11\x24\x7e\x60\xc7\x50\x2d\x15\x64\xcb\x85\xc3\xc2\x70\x49\x85\xb4\x30\x5c\x53\xdf\x93\xa0\xed\x58\x98\x3f\x0b\x47\xf5\x82\x38\x1e\x65\x7d\x55\x8b\xbd\xa7\xb6\xaf\x65\x4d\x9a\x1c\x7f\x13\x34\xe6\xf7\xb0\x15\xd9\xbc\xa9\x8e\xf5\x5e\x3a\xa3\x0e\xe2\xaa\xc2\xca\x87\xe9\x4c\xb0\x06\x57\x2a\x23\xe7\x52\xa0\xd9\xdd\x2c\x63\xe8\x2d\x67\x94\x59\x06\xcf\x82\x7d\x2d\xc5\x93\xc5\xee\x39\x8c\xb8\x02\x62\x16\xdc\xa6\x93\xa4\x61\x6f\xa2\xa3\x30\xee\x1d\x3b\x9b\xce\xd1\x4f\x12\x3d\xc7\x44\xec\x75\x1c\xbb\xd3\x8b\xb5\x1c\x57\x60\x32\x3e\x8b\xad\xb8\x0a\xc1\x5d\xb0\xb7\xdb\xb6\xfc\x13\x18\x95\x35\xb7\x31\x09\xae\x67\xbf\xae\x9f\xf5\xa6\xe5\x17\xa7\x23\xfa\xf7\xfa\x0c\x85\x4b\x14\x31\xc4\xe5\x88\x56\x89\xac\x3a\xd3\xc2\xc7\x51\x44\x99\x40\x3e\xe8\x4f\x95\xc3\x7a\xf4\xf1\x2c\xee\x98\xcb\x13\xe4\xf7\x36\x90\xdf\xdf\xf4\xab\x78\x61\x67\xde\x6a\x7a\x57\x09\xf1\x2b\xa7\xa4\x67\x81\x7d\xa6\x1c\x6e\x76\xcb\xcd\xcd\xc7\x05\x0c\x51\xfe\x20\xa3\x1c\xa5\x51\xa6\x00\xcc\x0f\x05\xda\xd2\x2e\xf6\xd1\x6d\x1e\x39\x52\xbc\xd3\xe7\xc4\xd8\x2e\xc9\x8b\x1b\x2d\x32\xd6\xaa\x16\x4c\xd6\xb4\xc8\x22\x57\x86\xf7\x91\xf9\x67\x0e\xf9\xca\x3c\xc2\x1e\x25\xbd\x6c\xaa\x3a\x37\xd8\xa7\xcb\xf3\x38\x75\x23\xdb\x2f\x3f\x5a\x00\xfb\xf3\xe6\xe3\x5c\x35\x49\xef\x39\x83\x02\x0d\x29\xc3\xdf\x90\x3d\xe4\x63\xe7\xa5\x58\x68\x60\x04\xfb\x38\xc0\xf9\xc5\xe1\x3a\xcd\x69\x34\xce\x2b\x21\x4f\xce\xf7\x77\x45\xd6\x5d\x52\x54\xa4\x41\x93\xe7\x48\x29\x9c\x24\x50\xad\x2e\x98\xf3\x20\x31\x6f\x97\x9b\xe8\x62\x3b\x04\x60\xce\x8c\xcc\x41\x4b\x17\xcc\x00\xa3\xc0\x77\xcb\x42\x4e\x03\x01\x53\xe9\xfd\x38\x04\xe4\xb7\xad\x3f\xc1\x7e\x2e\xc9\x2c\x0c\x92\x67\xca\xbb\x5f\xd9\x1c\xca\x9e\xcd\x5b\xc0\xc9\xac\x98\x98\xa8\xe4\x25\x42\x42\xa8\x80\xb9\x04\xa1\x9b\x5d\x0e\x56\x15\x0a\x71\xd1\xec\xb8\xb7\xd2\x92\x95\xec\x48\x9f\xcc\xe9\xe1\xb6\x3a\x9c\x76\x87\xb2\x3c\x24\xf8\xa2\x24\xfe\x73\x38\x61\x2e\xd1\xc8\x1a\xcb\x69\x95\x01\x22\x30\x8d\xf6\x2c\x5e\x20\xb0\x92\x32\x98\x31\xaf\x50\xfa\x92\x41\xf9\x13\xb7\x7f\x38\x85\xb2\x21\x24\x98\xc3\x38\xcf\xb2\x10\xac\x0f\x46\xdf\xc7\x95\xc1\x94\x22\xb9\x1c\x0c\x1b\xb9\xd2\x69\x94\xc3\x95\x4c\xe5\xd2\xc5\x1c\x72\x7e\x96\xa9\x60\xc9\xe1\xff\x3d\x90\x33\x27\x7e\x09\x24\xcb\xb2\x75\x47\xce\x63\xa4\xba\xfc\x2f\x73\x9f\xc4\x72\xc1\x9f\xf2\x93\x22\x8b\x57\x8a\x3a\x8b\x8d\x0a\x39\x57\xa4\xea\x62\x45\x57\xc0\x30\x03\x55\x37\xd3\xc8\x9c\x43\x3d\xdf\x85\x81\x55\x74\xdd\x9f\xc0\xa2\x30\x8e\xc5\x14\x30\xa1\xba\xa0\xaf\xd8\x63\x5e\x0c\xff\x47\x86\x10\x5d\x46\xc2\xa2\x8e\x73\xf5\x30\xa3\xf9\x65\xe1\x35\x08\x00\x7a\x88\x30\xcb\xc5\xbb\xe5\xf3\xaa\xac\x18\xb8\x14\xa6\xb0\xf6\xf5\xe4\x59\x66\xff\xa9\x55\xd0\x17\x46\x92\x02\x2c\x16\xa1\x71\xdc\xf3\xae\x3f\x68\x02\xa4\x14\x67\xef\x8a\x4a\x39\xab\x6f\x8c\x9a\x75\xcd\x79\x91\x67\x27\xd2\xa3\x66\xc8\xa3\x6c\x76\xc9\x47\xc6\x2f\x72\x30\x30\x73\x09\x94\xe3\x50\x9c\x79\x0a\x41\xe3\x60\x9c\x8e\xc8\xfe\x04\xb9\xfd\x43\xe3\x70\x88\x00\x26\x3e\x7a\xc8\x41\x1f\xc0\x80\xa3\xea\x58\xe6\xcf\xaa\x64\xcf\x46\x68\xbb\x17\xd4\xe2\x2a\x44\xf3\x50\x84\x46\xda\x38\xc3\x51\x8a\xf4\xc5\x38\xec\x23\x26\x59\xa9\x54\x13\xc0\x04\x20\xe8\x8d\x4c\xa2\x57\x48\x46\xf6\xf0\xc6\x8c\x8c\x66\x53\x13\x12\xdf\xfc\xe7\x54\x64\xff\x49\x7d\xda\xab\xf8\x38\xb3\xae\x65\x53\x9d\x40\x7f\x0a\x3c\x86\x05\x62\x18\x36\x94\x84\xf0\x29\x11\xf0\x41\xc7\x5d\x30\x4f\x45\x0d\x60\x6e\x20\x14\xe2\x00\x32\xe9\xfd\x8a\x4c\x17\x04\x6e\x12\xc0\x37\xc0\x0b\xe0\x98\xab\x20\x1e\x24\xe0\xea\xd7\x73\x9d\x0c\x08\x11\x11\xa9\xe7\x7b\x2a\xf9\xa6\x18\x9d\x38\xd6\xaa\xbf\x0e\x6d\x40\x32\x9d\x81\xb5\x7c\xc4\x1b\xed\x40\xf3\x14\xce\x5b\xca\x12\xd6\x6d\x49\xc4\x98\xba\xcd\x51\xea\x6a\xc3\x83\x94\xec\xe6\xe6\x00\x62\x84\xb0\x56\xf0\x5b\xd2\xa6\x53\x23\x0d\x68\x10\xd0\x7b\x4c\x86\x31\x61\x71\x51\x9c\x7c\x6e\x6e\x6e\xf8\x5d\x60\x39\x84\x00\x72\xcf\xfc\x9e\x36\xbe\x5e\x1c\x09\xd0\x83\xc4\xef\x25\x8a\xe1\x31\x28\x6d\x25\x40\x8a\xf1\x3b\xd3\x8c\x35\x67\x98\x6c\x0a\x9d\xef\xf7\x91\xbf\x05\x28\x03\x58\xb7\x51\x12\x07\x30\x07\x28\x8c\xc4\x74\x4b\xbe\x4b\xd5\x96\xae\xda\xe2\xe3\x40\x70\x00\x99\x35\x7f\x12\x9b\xc6\x4c\xae\xa3\x80\xfa\xc8\x3a\x49\x9b\x97\xf5\x8c\x28\x9b\xe2\x9e\x90\x56\x2b\x58\xa1\x7a\x09\xc7\x00\x1e\xbb\x0a\xb9\x98\x06\xa8\xab\x76\x35\xad\x2b\xd4\x55\x99\xee\x15\x96\x2e\x30\xd5\x28\x5d\x50\x86\x2c\x94\xaf\xac\x39\x2b\xea\x7e\x84\x18\xb2\x96\x53\x3a\xa4\xb5\xaa\xc0\x91\x94\x13\xe4\xc7\xab\x43\xea\x25\x05\x4e\xe3\x25\x27\xe7\x46\x72\xe9\x66\x0b\xdc\x18\x24\xc8\x3f\x63\x69\x91\xff\x54\x91\xd3\x9b\x2d\x00\x89\x0f\x6e\xe2\xc0\xf6\x4d\xba\xd0\x92\x21\xf4\x41\x29\xca\xf4\xa4\xdf\xfc\xcf\xdf\x65\xdf\xd7\x37\x4a\x6c\x6e\xce\xcf\x7e\x39\x75\xf4\xf1\x28\xf9\x3a\x26\x9e\xc0\x13\x94\xed\x7f\x74\x71\x72\xa3\x87\xfc\x70\x79\xd3\x00\x3f\xd1\x7b\x34\x41\x6c\x0b\x4c\xe9\x58\x29\x06\x49\x39\x04\x21\x7c\xc0\xe1\x38\x94\x3c\x68\x35\x53\x70\x94\x28\x5a\x61\x42\xa9\x12\x0b\x83\xfd\xa7\x33\x39\x73\xad\xce\x4c\xde\x48\xdf\xfd\x20\xf9\xa6\x24\xee\x06\xde\xf3\x3a\xbf\xe3\x75\x9d\x82\xd5\x48\xaa\x98\xab\x66\x0d\xb8\xd1\x75\x46\x37\x55\x97\xab\xbd\x56\x5f\x03\x1b\xbe\x02\x9f\x80\x7e\x6d\x17\x38\xa9\xee\xff\x8c\xea\xff\x72\x93\xa1\x4b\xb2\x71\x5c\x76\xac\xc9\x80\x7a\x14\xfd\xd3\x1e\x02\x32\xc1\xf5\x7b\x49\xd5\x92\x18\x07\xf8\x16\x49\xa4\xff\xd2\xde\xfd\x2e\x8a\x45\xa9\x4b\xf9\xd1\x9e\x16\x43\xdf\x40\xa1\xbe\x4b\x27\x1c\x8c\x20\x07\x11\x62\x21\xe6\x3c\xae\xc9\xe6\x08\x29\x91\xd2\x7c\x41\xbe\x21\x07\x17\x54\xa0\x46\x82\x9f\xde\x74\xd2\x73\xcb\x52\xe2\xe3\xaa\x16\xcc\x8d\xde\xc5\xea\x2b\x36\x1a\x94\xcc\x15\x28\x25\xb7\x02\x72\xec\xf1\x96\x7e\x01\x59\xb5\x57\x49\x4a\x6a\xcb\xa9\xb7\x8d\xf4\xea\x0b\x55\x6c\x94\xa0\x15\xdf\x7d\x61\x02\x45\x5d\xd0\x57\x6f\xe3\x97\xfa\x8f\xb7\xb1\x49\xfe\xf3\xe7\xeb\x0d\x73\xc4\x91\x10\x91\x84\x6e\x53\x9b\x2d\x08\x74\xde\xe5\x90\x89\x50\x6a\x46\xd7\xde\x4f\xad\xdf\x07\xb6\x2c\x82\x72\x00\xd8\xef\x82\x80\x0e\x7b\x1c\x93\xdb\x5e\xb3\xd1\x9a\x7d\xd0\xe7\x40\x2c\x48\xb3\x6f\x0b\x9d\x31\x51\x45\x41\x7c\xdb\x1c\xa4\x96\xc1\xff\x9c\x0e\xc1\x15\x26\xb7\xb3\xd7\x89\x9b\x05\x6a\x56\x6b\x57\x32\xb1\x9e\xd5\x04\x76\x26\x2b\x0b\x39\xcd\xb5\x2d\x89\x7f\x23\x22\xc3\x14\xa3\x7c\x32\xad\x0e\xb8\x39\x5e\x51\x2a\xab\xae\x8a\xca\x7a\xd9\xa2\xb2\xba\xab\xa8\x2c\x9f\xa0\x29\x3e\x84\x13\x86\x79\xd7\x30\x5d\x6a\xe9\x6d\x2d\xc9\x23\xb0\x08\xf4\x0c\x38\xdd\x45\x47\xb8\xbd\x2c\xe0\x0e\x40\x38\x0e\x04\xee\x05\x98\x38\x4f\xbf\xcf\xca\x53\xcd\x35\x6f\x37\x30\x26\xef\xbd\x84\x05\xce\x31\x71\xb5\x8c\x11\x2f\x6f\xa3\x68\xe8\x53\x1a\x20\x48\x1c\xdf\x1f\xea\x43\x46\xc7\x51\x17\xd4\x10\xf1\x23\x8a\x89\xc8\x9f\x87\xe2\x23\x7a\xdf\x83\x41\xf0\x78\x72\xae\x46\xf4\x5e\x6e\xf8\xc5\xc4\x94\xb5\x78\x24\x29\x82\x46\xd8\x9b\x93\x85\xa7\x61\x28\x0d\x05\xb9\x3d\x09\xe4\xcf\x8e\x7f\xe8\xdd\x53\x01\xd0\x11\x1f\xb7\x08\x5d\x17\x37\x28\x4e\xb8\xa4\x68\xab\x55\x67\xe3\xcc\x05\x8a\x1e\x1f\x0c\xcb\x44\xe7\xd3\xa7\x5e\x2a\xc8\x31\x4c\xc2\x11\x13\x3d\x65\x35\x16\xb5\x29\xf6\x2b\xf3\xcf\x91\xef\xab\xd2\x99\x31\x17\x34\xd4\xc6\x68\x62\x8e\x78\x54\xd9\x27\x22\xde\xfa\x63\x83\x37\x44\x9c\xeb\x40\x00\x10\x0c\x12\x8e\x45\x36\x37\x9a\x3e\xf3\xc9\x91\xcf\x1c\x5a\x72\xf4\x5c\x27\xf6\x5e\x6c\x74\x6b\xa4\x05\x95\x1e\x29\xf4\x7d\xe4\x97\x82\x8a\x85\xe3\xad\xec\x54\xde\xb0\x58\x48\xcc\xa7\x20\x43\x57\x8a\xbd\x66\xa8\x89\x7e\x15\x94\x7f\x53\xc9\xba\x47\xa3\x5c\x94\x22\x34\x9f\xfa\x5c\xac\x92\xdc\xe1\x1c\x9c\xcf\x94\xb8\x6a\x6e\x83\x23\x4f\x64\x83\x68\x79\xec\x9d\x0a\xbe\x1a\xe6\x75\x6b\x75\x38\x1b\xcd\x19\xa3\xca\x0a\x44\x0f\x82\x41\x6f\xb1\x25\x78\xaa\xfb\x00\x18\x0b\xeb\x80\xd1\x50\x4d\x7e\x9f\xfa\x59\xad\x91\x3e\x7f\xfc\xe5\xb3\x0a\x59\x8c\x31\x4a\x58\xfc\x54\xa2\x66\x89\xc1\xf7\x92\xb5\x11\xe4\xbd\x11\x82\x3e\x62\xbd\x01\x0e\x04\xca\x55\xd4\xa6\x8f\x35\xc7\x6f\x55\x63\xd0\x87\x5c\xba\xff\x3a\xb4\xa0\x0b\x25\x3d\x35\xef\x94\x20\xa0\xe1\x3e\x52\xf8\xdc\x05\x0d\x85\x78\x49\xd9\xd3\xe3\xc6\xce\x2e\x4d\xf2\x6d\xe5\x8a\x2d\x39\xf1\x1e\x77\xbe\xc8\xd7\x32\xd8\x4f\x2c\x13\x3f\xe9\xa1\xe6\x37\x5f\x9d\xac\x3a\xca\x2c\xf2\x68\x41\x9e\xa0\x16\x4f\xd4\xf7\x17\xd7\x9c\x24\x55\x13\xd9\xd4\x05\xac\xec\xfb\xbd\x9f\x9e\xd3\xa1\x99\x75\x9a\x73\x36\x22\x73\xbe\xff\xd8\xbe\xee\x02\xd8\x69\x29\x50\x3b\xec\xf3\x49\x93\xef\x0b\x82\xf6\x87\xcd\xf6\x70\xb4\x3b\xec\x18\xee\x4f\xee\x5c\x91\xd1\x67\xaf\xcf\x06\xac\xd9\x6c\x47\x03\x72\x3b\x6a\xda\x03\x24\xd7\xaf\x80\x1a\x67\x13\xaf\x0e\x3d\x4f\xd4\x5b\x7b\x6d\x34\x68\xfb\x07\xf5\x66\xbb\x79\x58\xef\xb4\x5a\xfb\xf5\x83\xce\x5e\xbb\xee\x0f\xf6\x76\xbc\x76\xb3\xbd\xeb\xb5\xf7\x1c\x50\xe2\xab\x59\x40\xad\xdf\xea\x74\xfc\xc3\xc3\x56\xbd\x79\x80\xfa\xf5\x4e\x67\xbf\x5d\x3f\x40\x5e\xab\x8e\xfa\xcd\x9d\x8e\xb7\x77\xd8\xde\x69\xf5\xcd\xfe\x63\x16\x74\x41\x6d\x40\x69\xdd\x85\x6f\xe3\x16\xf2\x06\xf4\x42\xd4\xf0\x68\xd8\xed\x74\x76\x6a\x19\x77\xcc\x79\x5e\xc9\x20\xbf\x79\x7b\x10\x90\x61\x73\xa7\xc5\xd1\xe1\x5d\x05\xf2\x51\xb3\xbd\xdb\xde\xdb\x45\x75\x78\x70\x00\xeb\x9d\xce\xa0\x5f\x3f\xe8\xec\x36\xeb\xc8\x6f\xb6\x9a\xa8\xbf\xd7\xf7\x76\xbd\x32\xf2\x7d\x6f\x17\x1e\xb4\x0f\x0f\xea\x7d\xe4\xef\xd7\x3b\xed\x36\xaa\x1f\x1c\x76\xf6\xeb\x83\xbd\x81\x0f\xf7\x0e\xdb\x87\xed\xc1\x20\x4f\x7e\x1f\xb2\x98\xfc\x76\x38\xf0\x60\xb3\xd9\x16\x87\x77\xfb\x7c\xd8\xe0\xac\x88\xfc\xe4\xec\x4e\xd6\xef\xce\x9f\x02\x02\x35\xb7\xd3\xef\x3c\x8f\xe5\x72\x5d\x67\xbe\x97\x19\x5b\xd2\x4f\xea\x67\xf2\xdc\xd7\xd8\xd7\x51\x93\xbb\xd5\x87\xf6\xfd\xc8\x33\xaf\xdb\x1e\xea\x16\x4d\xb3\xab\x39\x49\xa8\xd6\xae\xae\x2f\xcf\x2e\xde\xd9\xbe\x89\xd3\x0e\x9d\xf5\xf8\xf9\xea\xc3\x45\xe6\xd2\x8c\xd8\xa9\xcf\xa5\x43\x4b\x1d\x8c\x38\xbc\xa3\xbe\x4a\xb5\x9a\x77\x4f\x93\x60\x98\x6a\xa2\x4c\xd6\xa2\xe3\x4c\x99\x73\x90\x2a\x9e\xd7\x4b\x4e\xa9\x99\x43\xfb\x08\xfa\xbd\x00\x09\xa9\x03\xee\xc6\x28\x4b\xa6\xe2\xae\x14\xb8\xe0\x2e\x13\x6d\x2a\xff\x65\xcc\xa2\xc8\x95\xe3\x7a\xce\x05\x34\x50\x41\x55\x61\xf2\x13\x41\x35\x3b\xbc\xd3\x80\x11\x6e\xd0\x08\x11\x3e\xc2\x03\x21\x65\x7b\x3b\x62\x74\x80\x03\xe4\x9a\x5d\x50\x8b\x1d\xfc\xba\xd5\x68\x81\x9b\x57\x8b\x68\x96\x1d\x1c\x74\x3f\x03\x31\xc5\x17\x88\x3a\x02\x85\xb5\x56\xd3\x50\x04\xf1\x4e\x92\xb9\x1e\xb1\x3c\xb6\xa6\xaf\x0d\xdd\xb6\xe0\xa8\x4b\xeb\x40\xed\xf8\xc3\xc5\xc5\xe9\xf1\xf5\x87\xcb\xfa\xfb\x77\xef\xaf\xeb\x56\x93\xf8\xaa\x3a\x50\xbb\x9a\x12\x6f\xc4\x28\xa1\x63\x0e\xa0\xda\xc5\x01\xe6\x80\x50\x91\x56\x7e\xeb\xd8\x3d\xe4\x53\xe2\xbd\x96\x8a\x21\x7f\xdd\x46\xe6\x2e\x3b\x50\x6b\xe1\xcf\x67\x38\xbc\x7b\xe7\xb1\x93\xf1\xf9\x5e\x0b\x7e\x7a\x38\xfb\xc7\xdd\x9b\xeb\xbb\x8b\x4b\x38\xe3\xd2\x99\x8e\x85\xff\x3a\x46\x6c\x5a\x81\x53\xed\x15\x71\xaa\x3d\x97\x51\x6d\x07\x9f\xfe\x63\x08\xc0\x5b\x75\xb8\x59\xda\x7e\x11\x64\x1c\x59\x99\xa0\x2e\xf8\x44\xa4\x12\x97\x5f\x55\xb8\x47\xc7\x7a\xe2\x72\x2b\xae\xee\x9e\x80\x11\xee\xe9\x90\x68\x7c\xee\xb7\x0b\x72\x18\x74\x17\x18\x2f\x2d\xd1\xf7\x68\x30\x0e\x89\x36\x4d\xe5\x48\x71\xa8\x1f\x6c\x62\x7f\xb3\x01\xae\x5c\xed\x54\x4e\xcc\x1c\x4d\xee\xc2\x94\x6c\xc5\x99\x6a\x2f\xa0\x63\xbf\x17\xe7\x53\x58\xf2\x56\x1f\xd5\x6b\x80\x5f\x75\x5e\x43\x4f\x64\x17\x60\x1f\xbc\x06\xad\xf6\x4e\xa1\x54\x04\x9f\x4f\xde\x8d\xa7\xfd\x33\x76\x4a\x1e\xd8\x11\x0a\xf7\xdb\x9d\xe1\xdd\xed\x2d\x3e\x99\x24\x52\x91\xbd\x1d\xd5\x25\x09\x9d\x66\x67\x25\x92\xb0\x3f\x4f\x10\xf6\x1d\xeb\xa5\xca\x15\xab\x33\x62\x9c\xb7\xa1\xbb\x48\xda\x7f\x3e\x82\xd2\xd4\x97\x0a\x9b\x61\xff\xf5\x66\x0b\xff\xb2\xe3\x8f\x7f\xfb\x72\x36\x99\xec\x7e\x99\x9c\x07\xd3\x6f\xad\xf0\xdd\xe5\xce\xcf\xd3\xbb\x8b\x4d\xa5\x1a\x06\x74\x4c\xfc\x92\xc5\xff\xe5\xc3\xfe\xb0\x3d\xdc\xfb\xe9\xda\xff\xf4\xcb\x27\xd8\xbe\xe5\x3f\x1d\xb4\x6f\x7f\x3d\xd9\x99\x26\x9c\xc9\xde\x14\xec\x54\x8d\xad\xd5\x68\xc6\xd6\x5c\xc5\xd8\x72\xb0\x25\x5d\xc6\x13\xc4\xf0\x60\x0a\x7e\xfe\x7c\xad\xef\x1f\xee\x82\xcb\xd8\x5b\x01\x70\x2c\x46\x94\xe1\x6f\xc9\x25\x4d\xb7\x88\x54\xe3\xcf\xce\xa7\xd1\xe9\xe8\x3e\xfc\xfd\x4d\xf4\xf9\xe3\xe0\xac\x1d\x5c\xa0\xdb\xc8\xef\xfc\xe3\x24\xe1\xcf\xa1\xdc\xcb\x8e\x29\x19\x04\xd8\x13\x15\x78\xb5\xb3\xb7\x12\x5e\x99\x60\xdc\xbc\x32\x5b\x98\x22\xa4\x8f\x04\x69\xcd\x83\x39\x80\x81\xb2\x8d\xd4\xc9\x95\x42\x3e\xec\xdd\x7e\x69\x7e\xc2\xa7\xb7\xdf\x6e\x7f\x3f\xfe\xf6\xf9\x23\x3a\x6b\xd3\x2f\x68\xe4\xef\x9c\xc6\x6c\xc8\xdf\xfb\xeb\x22\xfd\x70\x25\x94\x1f\xce\x23\xfc\xd0\x29\x23\xe9\xcf\x36\x20\x7b\xd0\xdc\x94\xa3\xd3\xf3\xc9\xdb\xc3\xaf\xef\x7f\xfd\xb2\xf7\x65\x38\x1a\xbc\x3f\x1c\xbe\xbb\xe4\x3f\x4d\x4e\x3f\xcf\x68\xad\xac\x2c\x9e\x8f\x62\x73\x17\x54\x63\xce\xee\xf5\x00\xd2\x3a\xe0\xd2\x6f\xfa\x70\xfc\xbe\x7e\xfa\x7b\xfd\xb0\x1b\x5f\x02\x22\x97\x90\xbe\xea\x23\x6d\x83\x1e\x44\x3d\xde\xfb\x60\x84\xeb\x2d\xfc\xd0\xdc\x09\x88\x1f\x84\x77\xcd\xbb\x81\xb7\xcf\xb1\x80\xbb\x3c\xf8\x3a\x39\x30\x9d\x10\xb9\xa1\x25\xb7\x2b\x4a\x3e\xb4\x86\xbb\xfe\xc1\xc1\x5d\x33\x60\x9e\x3f\xe9\x0c\xf7\x61\xd0\xdf\xe7\xc1\x60\x48\xbe\xee\xf8\xa3\x3e\xff\xfa\x97\xff\xf7\xd7\xd3\xdf\xaf\x2f\x8f\xc0\x7f\x69\x8a\x1b\x0a\xe3\xd7\xd8\x47\x44\xc8\x39\x33\x23\x08\x98\x83\xcd\x4e\xb3\xb3\xb9\xa5\x78\xa1\xfe\x3c\x3e\xff\x74\x75\x7d\x7a\x79\xa5\x99\x21\x3f\xaa\x4c\xf8\x6c\x62\x41\x0a\x48\xb5\x6f\x0d\x77\x29\xdb\x6d\x4e\xf0\xb8\xb9\x4f\x91\x9c\xb6\x11\xbb\xf5\xda\x7b\xfe\x70\x20\xbe\xb6\xa0\xb7\x69\x6e\xb2\x71\x90\x40\xf5\x2a\x25\xc2\xd0\xb7\x7f\x2b\xd1\x27\xd7\xfc\x33\x9b\xee\x11\x7e\xd7\x6f\xf3\x8b\xf0\xed\xd7\xdd\xfe\xef\xd1\xc9\xfe\x31\xac\x6d\xfc\x5f\x00\x00\x00\xff\xff\xa2\x8b\x55\x65\x78\xd6\x00\x00") func connector_mgmtYamlBytes() ([]byte, error) { return bindataRead( @@ -93,7 +93,7 @@ func connector_mgmtYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "connector_mgmt.yaml", size: 41252, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + info := bindataFileInfo{name: "connector_mgmt.yaml", size: 54904, mode: os.FileMode(420), modTime: time.Unix(1, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/internal/connector/internal/handlers/connector_admin.go b/internal/connector/internal/handlers/connector_admin.go index aaf8ad868..5bd9ab644 100644 --- a/internal/connector/internal/handlers/connector_admin.go +++ b/internal/connector/internal/handlers/connector_admin.go @@ -6,7 +6,6 @@ import ( "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/auth" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/client/keycloak" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/server" - "net/http" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/dbapi" @@ -23,13 +22,14 @@ import ( type ConnectorAdminHandler struct { di.Inject - Bus signalbus.SignalBus - Service services.ConnectorClusterService - Keycloak coreservices.KafkaKeycloakService - ConnectorTypes services.ConnectorTypesService - Vault vault.VaultService - KeycloakConfig *keycloak.KeycloakConfig - ServerConfig *server.ServerConfig + Bus signalbus.SignalBus + Service services.ConnectorClusterService + NamespaceService services.ConnectorNamespaceService + Keycloak coreservices.KafkaKeycloakService + ConnectorTypes services.ConnectorTypesService + Vault vault.VaultService + KeycloakConfig *keycloak.KeycloakConfig + ServerConfig *server.ServerConfig } func NewConnectorAdminHandler(handler ConnectorAdminHandler) *ConnectorAdminHandler { @@ -57,9 +57,9 @@ func (h *ConnectorAdminHandler) ListConnectorClusters(w http.ResponseWriter, r * Total: int32(paging.Total), } - for _, resource := range resources { - converted := presenters.PresentPrivateConnectorCluster(resource) - resourceList.Items = append(resourceList.Items, converted) + resourceList.Items = make([]private.ConnectorCluster, len(resources)) + for i, resource := range resources { + resourceList.Items[i] = presenters.PresentPrivateConnectorCluster(resource) } return resourceList, nil @@ -86,8 +86,8 @@ func (h *ConnectorAdminHandler) GetConnectorUpgradesByType(writer http.ResponseW return nil, serviceError } result := make([]private.ConnectorAvailableTypeUpgrade, len(upgrades)) - for i, upgrade := range upgrades { - result[i] = *presenters.PresentConnectorAvailableTypeUpgrade(&upgrade) + for j, upgrade := range upgrades { + result[j] = *presenters.PresentConnectorAvailableTypeUpgrade(&upgrade) } i = private.ConnectorAvailableTypeUpgradeList{ @@ -103,19 +103,6 @@ func (h *ConnectorAdminHandler) GetConnectorUpgradesByType(writer http.ResponseW handlers.HandleGet(writer, request, &cfg) } -func isAdmin(request *http.Request) *errors.ServiceError { - ctx := request.Context() - _, err := auth.GetClaimsFromContext(ctx) - if err != nil { - return errors.NewWithCause(errors.ErrorUnauthenticated, err, "user not authenticated") - } - - if !auth.GetIsAdminFromContext(ctx) { - return errors.NewWithCause(errors.ErrorUnauthenticated, err, "user not authorized") - } - return nil -} - func (h *ConnectorAdminHandler) UpgradeConnectorsByType(writer http.ResponseWriter, request *http.Request) { resource := make([]private.ConnectorAvailableTypeUpgrade, 0) id := mux.Vars(request)["connector_cluster_id"] @@ -196,3 +183,99 @@ func (h *ConnectorAdminHandler) UpgradeConnectorsByOperator(writer http.Response handlers.Handle(writer, request, &cfg, http.StatusNoContent) } + +func (h *ConnectorAdminHandler) GetConnectorNamespaces(writer http.ResponseWriter, request *http.Request) { + listArgs := coreservices.NewListArguments(request.URL.Query()) + cfg := handlers.HandlerConfig{ + Action: func() (interface{}, *errors.ServiceError) { + if err := isAdmin(request); err != nil { + return nil, err + } + + namespaces, paging, err := h.NamespaceService.List(request.Context(), []string{}, listArgs) + if err != nil { + return nil, err + } + + result := private.ConnectorNamespaceList{ + Kind: "ConnectorNamespaceList", + Page: int32(paging.Page), + Size: int32(paging.Size), + Total: int32(paging.Total), + } + + result.Items = make([]private.ConnectorNamespace, len(namespaces)) + for i, namespace := range namespaces { + result.Items[i] = presenters.PresentPrivateConnectorNamespace(namespace) + } + + return result, nil + }, + } + + handlers.HandleGet(writer, request, &cfg) +} + +func (h *ConnectorAdminHandler) CreateConnectorNamespace(writer http.ResponseWriter, request *http.Request) { + var resource private.ConnectorNamespaceWithTenantRequest + cfg := handlers.HandlerConfig{ + MarshalInto: &resource, + Action: func() (i interface{}, serviceError *errors.ServiceError) { + if serviceError = isAdmin(request); serviceError != nil { + return nil, serviceError + } + + ctx := request.Context() + connectorNamespace := presenters.ConvertConnectorNamespaceWithTenantRequest(&resource) + if connectorNamespace.TenantUserId != nil { + connectorNamespace.Owner = *connectorNamespace.TenantUserId + } else { + // NOTE: admin user is owner + claims, err := auth.GetClaimsFromContext(ctx) + if err != nil { + return nil, errors.NewWithCause(errors.ErrorUnauthenticated, err, "user not authenticated") + } + connectorNamespace.Owner = auth.GetUsernameFromClaims(claims) + } + if err := h.NamespaceService.Create(ctx, connectorNamespace); err != nil { + return nil, err + } + i = presenters.PresentPrivateConnectorNamespace(connectorNamespace) + return + }, + } + + handlers.Handle(writer, request, &cfg, http.StatusCreated) +} + +func (h *ConnectorAdminHandler) DeleteConnectorNamespace(writer http.ResponseWriter, request *http.Request) { + namespaceId := mux.Vars(request)["namespace_id"] + cfg := handlers.HandlerConfig{ + Validate: []handlers.Validate{ + handlers.Validation("namespace_id", &namespaceId, handlers.MinLen(1), handlers.MaxLen(maxConnectorNamespaceIdLength)), + }, + Action: func() (i interface{}, serviceError *errors.ServiceError) { + if serviceError = isAdmin(request); serviceError != nil { + return nil, serviceError + } + + serviceError = h.NamespaceService.Delete(request.Context(), namespaceId) + return nil, serviceError + }, + } + + handlers.HandleDelete(writer, request, &cfg, http.StatusNoContent) +} + +func isAdmin(request *http.Request) *errors.ServiceError { + ctx := request.Context() + _, err := auth.GetClaimsFromContext(ctx) + if err != nil { + return errors.NewWithCause(errors.ErrorUnauthenticated, err, "user not authenticated") + } + + if !auth.GetIsAdminFromContext(ctx) { + return errors.NewWithCause(errors.ErrorUnauthenticated, err, "user not authorized") + } + return nil +} diff --git a/internal/connector/internal/handlers/connector_namespace.go b/internal/connector/internal/handlers/connector_namespace.go new file mode 100644 index 000000000..b7ab86045 --- /dev/null +++ b/internal/connector/internal/handlers/connector_namespace.go @@ -0,0 +1,286 @@ +package handlers + +import ( + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/dbapi" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/db" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/services/signalbus" + "net/http" + + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/public" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/presenters" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/services" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/handlers" + "github.com/goava/di" + + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/auth" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/errors" + coreservices "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/services" + "github.com/gorilla/mux" +) + +var ( + maxConnectorNamespaceIdLength = 32 + maxConnectorNamespaceNameLength = 63 + defaultNamespaceName = "default_namespace" +) + +type ConnectorNamespaceHandler struct { + di.Inject + Bus signalbus.SignalBus + Service services.ConnectorNamespaceService +} + +func NewConnectorNamespaceHandler(handler ConnectorNamespaceHandler) *ConnectorNamespaceHandler { + return &handler +} + +func (h *ConnectorNamespaceHandler) Create(w http.ResponseWriter, r *http.Request) { + var resource public.ConnectorNamespaceRequest + cfg := &handlers.HandlerConfig{ + MarshalInto: &resource, + Validate: []handlers.Validate{ + handlers.Validation("name", &resource.Name, handlers.WithDefault(defaultNamespaceName), + handlers.MinLen(1), handlers.MaxLen(maxConnectorNamespaceNameLength)), + }, + Action: func() (interface{}, *errors.ServiceError) { + + convResource := presenters.ConvertConnectorNamespaceRequest(&resource) + + ctx := r.Context() + claims, err := auth.GetClaimsFromContext(ctx) + if err != nil { + return nil, errors.Unauthenticated("user not authenticated") + } + userID := auth.GetUsernameFromClaims(claims) + convResource.Owner = userID + organisationId := auth.GetOrgIdFromClaims(claims) + if convResource.ClusterId == "" { + if err := h.Service.SetTenantClusterId(convResource, organisationId); err != nil { + return nil, err + } + } else { + // TODO move auth checks in an orthogonal feature + if err := h.checkAuthorizedAccess(userID, convResource, organisationId); err != nil { + return nil, err + } + } + + // set tenant to org if there is one, or set it to user + if auth.GetFilterByOrganisationFromContext(ctx) { + convResource.TenantOrganisationId = &organisationId + convResource.TenantOrganisation = &dbapi.ConnectorTenantOrganisation{ + Model: db.Model{ + ID: organisationId, + }, + } + } else { + convResource.TenantUserId = &userID + convResource.TenantUser = &dbapi.ConnectorTenantUser{ + Model: db.Model{ + ID: userID, + }, + } + } + + if err := h.Service.Create(ctx, convResource); err != nil { + return nil, err + } + return presenters.PresentConnectorNamespace(convResource), nil + }, + } + + // return 201 status created + handlers.Handle(w, r, cfg, http.StatusCreated) +} + +func (h *ConnectorNamespaceHandler) checkAuthorizedAccess(userID string, convResource *dbapi.ConnectorNamespace, + organisationId string) *errors.ServiceError { + + var ownerClusterIds []string + if err := h.Service.GetOwnerClusterIds(userID, &ownerClusterIds); err != nil { + return err + } + authorized := false + for _, id := range ownerClusterIds { + if id == convResource.ClusterId { + authorized = true + break + } + } + if !authorized { + var orgClusterIds []string + if err := h.Service.GetOrgClusterIds(userID, organisationId, &orgClusterIds); err != nil { + return err + } + for _, id := range orgClusterIds { + if id == convResource.ClusterId { + authorized = true + break + } + } + if !authorized { + return errors.Unauthorized( + "user %v is not authorized to create namespace in cluster %v", + userID, convResource.ClusterId) + } + } + + return nil +} + +func (h *ConnectorNamespaceHandler) CreateEvaluation(w http.ResponseWriter, r *http.Request) { + var resource public.ConnectorNamespaceEvalRequest + cfg := &handlers.HandlerConfig{ + MarshalInto: &resource, + Validate: []handlers.Validate{ + handlers.Validation("name", &resource.Name, handlers.WithDefault(defaultNamespaceName), + handlers.MinLen(1), handlers.MaxLen(maxConnectorNamespaceNameLength)), + }, + Action: func() (interface{}, *errors.ServiceError) { + + convResource := presenters.ConvertConnectorNamespaceEvalRequest(&resource) + + claims, err := auth.GetClaimsFromContext(r.Context()) + if err != nil { + return nil, errors.Unauthenticated("user not authenticated") + } + userId := auth.GetUsernameFromClaims(claims) + convResource.Owner = userId + if err := h.Service.SetEvalClusterId(convResource); err != nil { + return nil, err + } + + // set tenant user for eval namespace + convResource.TenantUserId = &convResource.Owner + convResource.TenantUser = &dbapi.ConnectorTenantUser{ + Model: db.Model{ + ID: userId, + }, + } + if err := h.Service.Create(r.Context(), convResource); err != nil { + return nil, err + } + return presenters.PresentConnectorNamespace(convResource), nil + }, + } + + // return 201 status created + handlers.Handle(w, r, cfg, http.StatusCreated) +} + +func (h *ConnectorNamespaceHandler) Get(w http.ResponseWriter, r *http.Request) { + connectorNamespaceId := mux.Vars(r)["connector_namespace_id"] + cfg := &handlers.HandlerConfig{ + Validate: []handlers.Validate{ + handlers.Validation("connector_namespace_id", &connectorNamespaceId, + handlers.MinLen(1), handlers.MaxLen(maxConnectorNamespaceIdLength)), + }, + Action: func() (i interface{}, serviceError *errors.ServiceError) { + resource, err := h.Service.Get(r.Context(), connectorNamespaceId) + if err != nil { + return nil, err + } + return presenters.PresentConnectorNamespace(resource), nil + }, + } + handlers.HandleGet(w, r, cfg) +} + +func (h *ConnectorNamespaceHandler) Update(w http.ResponseWriter, r *http.Request) { + var resource public.ConnectorNamespaceRequest + + connectorNamespaceId := mux.Vars(r)["connector_namespace_id"] + cfg := &handlers.HandlerConfig{ + MarshalInto: &resource, + Validate: []handlers.Validate{ + handlers.Validation("connector_namespace_id", &connectorNamespaceId, + handlers.MinLen(1), handlers.MaxLen(maxConnectorNamespaceIdLength)), + }, + Action: func() (i interface{}, serviceError *errors.ServiceError) { + existing, err := h.Service.Get(r.Context(), connectorNamespaceId) + if err != nil { + return nil, err + } + + // Copy over the fields that support being updated... + existing.Name = resource.Name + + return nil, h.Service.Update(r.Context(), existing) + }, + } + handlers.Handle(w, r, cfg, http.StatusNoContent) +} + +func (h *ConnectorNamespaceHandler) Delete(w http.ResponseWriter, r *http.Request) { + connectorNamespaceId := mux.Vars(r)["connector_namespace_id"] + cfg := &handlers.HandlerConfig{ + Validate: []handlers.Validate{ + handlers.Validation("connector_namespace_id", &connectorNamespaceId, + handlers.MinLen(1), handlers.MaxLen(maxConnectorNamespaceIdLength)), + }, + Action: func() (i interface{}, serviceError *errors.ServiceError) { + err := h.Service.Delete(r.Context(), connectorNamespaceId) + return nil, err + }, + } + handlers.HandleDelete(w, r, cfg, http.StatusNoContent) +} + +func (h *ConnectorNamespaceHandler) List(w http.ResponseWriter, r *http.Request) { + cfg := &handlers.HandlerConfig{ + Action: func() (i interface{}, serviceError *errors.ServiceError) { + + ctx := r.Context() + listArgs := coreservices.NewListArguments(r.URL.Query()) + + claims, err := auth.GetClaimsFromContext(r.Context()) + if err != nil { + return nil, errors.Unauthenticated("user not authenticated") + } + // TODO handle creating default namespaces in List() + userId := auth.GetUsernameFromClaims(claims) + var ownerClusterIds []string + if err := h.Service.GetOwnerClusterIds(userId, &ownerClusterIds); err != nil { + return nil, err + } + organisationId := auth.GetOrgIdFromClaims(claims) + var orgClusterIds []string + if err := h.Service.GetOrgClusterIds(userId, organisationId, &orgClusterIds); err != nil { + return nil, err + } + + clusterIds := append(ownerClusterIds, orgClusterIds...) + if len(clusterIds) == 0 { + // user doesn't have access to any clusters + return public.ConnectorNamespaceList{ + Kind: "ConnectorNamespaceList", + Page: 1, + Size: 0, + Total: 0, + Items: make([]public.ConnectorNamespace, 0), + }, nil + } + + resources, paging, serviceError := h.Service.List(ctx, clusterIds, listArgs) + if serviceError != nil { + return nil, serviceError + } + + items := make([]public.ConnectorNamespace, len(resources)) + for j, resource := range resources { + items[j] = presenters.PresentConnectorNamespace(resource) + } + resourceList := public.ConnectorNamespaceList{ + Kind: "ConnectorNamespaceList", + Page: int32(paging.Page), + Size: int32(paging.Size), + Total: int32(paging.Total), + Items: items, + } + + return resourceList, nil + }, + } + + handlers.HandleList(w, r, cfg) +} diff --git a/internal/connector/internal/migrations/202202070000_add_connector_namespace_tables.go b/internal/connector/internal/migrations/202202070000_add_connector_namespace_tables.go new file mode 100644 index 000000000..40b5b170a --- /dev/null +++ b/internal/connector/internal/migrations/202202070000_add_connector_namespace_tables.go @@ -0,0 +1,91 @@ +package migrations + +// Migrations should NEVER use types from other packages. Types can change +// and then migrations run on a _new_ database will fail or behave unexpectedly. +// Instead of importing types, always re-create the type in the migration, as +// is done here, even though the same type is defined in pkg/api + +import ( + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/db" + "github.com/go-gormigrate/gormigrate/v2" + "time" +) + +func addConnectorNamespaceTables(migrationId string) *gormigrate.Migration { + + type ConnectorTenantUser struct { + db.Model // user id in Id, required for references and data consistency + } + + type ConnectorTenantOrganisation struct { + db.Model // org id in Id, required for references and data consistency + } + + type ConnectorNamespaceAnnotation struct { + NamespaceId string `gorm:"primaryKey;index"` + Name string `gorm:"primaryKey;not null"` + Value string `gorm:"not null"` + } + + type ConnectorNamespace struct { + db.Model + Name string `gorm:"not null;uniqueIndex:idx_connector_namespaces_name_cluster_id"` + ClusterId string `gorm:"not null;uniqueIndex:idx_connector_namespaces_name_cluster_id;index"` + + Owner string `gorm:"not null;index"` + Version int64 `gorm:"type:bigserial;index"` + Expiration *time.Time + + // metadata + Annotations []ConnectorNamespaceAnnotation `gorm:"foreignKey:NamespaceId;references:ID"` + + // tenant, only one of the below fields can be not null + TenantUserId *string `gorm:"index:connector_namespaces_user_organisation_idx;index:,where:tenant_user_id is not null"` + TenantOrganisationId *string `gorm:"index:connector_namespaces_user_organisation_idx;index:,where:tenant_organisation_id is not null"` + TenantUser *ConnectorTenantUser `gorm:"foreignKey:TenantUserId"` + TenantOrganisation *ConnectorTenantOrganisation `gorm:"foreignKey:TenantOrganisationId"` + } + + return db.CreateMigrationFromActions(migrationId, + db.CreateTableAction(&ConnectorTenantUser{}), + db.CreateTableAction(&ConnectorTenantOrganisation{}), + db.CreateTableAction(&ConnectorNamespaceAnnotation{}), + db.CreateTableAction(&ConnectorNamespace{}), + + // connector namespace version + db.ExecAction(` + CREATE OR REPLACE FUNCTION connector_namespaces_version_trigger() RETURNS TRIGGER LANGUAGE plpgsql AS ' + BEGIN + NEW.version := nextval(''connector_namespaces_version_seq''); + RETURN NEW; + END;' + `, ` + DROP FUNCTION IF EXISTS connector_namespaces_version_trigger + `), + db.ExecAction(`DROP TRIGGER IF EXISTS connector_namespaces_version_trigger ON connector_namespaces`, ``), + db.ExecAction(` + CREATE TRIGGER connector_namespaces_version_trigger BEFORE INSERT OR UPDATE ON connector_namespaces + FOR EACH ROW EXECUTE PROCEDURE connector_namespaces_version_trigger(); + `, ` + DROP TRIGGER IF EXISTS connector_namespaces_version_trigger ON connector_namespaces + `), + + // foreign key relationship between namespaces and clusters + db.ExecAction( + "ALTER TABLE connector_namespaces DROP CONSTRAINT IF EXISTS fk_connector_namespaces_cluster_id", + ""), + db.ExecAction( + "ALTER TABLE connector_namespaces ADD CONSTRAINT fk_connector_namespaces_cluster_id "+ + "FOREIGN KEY (cluster_id) REFERENCES connector_clusters(id)", + "ALTER TABLE connector_namespaces DROP CONSTRAINT IF EXISTS fk_connector_namespaces_cluster_id"), + + // only one of tenantUser or tenantOrg in ConnectorNamespace can be not null + db.ExecAction( + "ALTER TABLE connector_namespaces DROP CONSTRAINT IF EXISTS connector_namespaces_tenant_check", + ""), + db.ExecAction( + "ALTER TABLE connector_namespaces ADD CONSTRAINT connector_namespaces_tenant_check "+ + "CHECK (((tenant_user_id is not null)::integer + (tenant_organisation_id is not null)::integer) = 1)", + "ALTER TABLE connector_namespaces DROP CONSTRAINT IF EXISTS connector_namespaces_tenant_check"), + ) +} diff --git a/internal/connector/internal/migrations/migrations.go b/internal/connector/internal/migrations/migrations.go index 7631faee9..348188d4e 100644 --- a/internal/connector/internal/migrations/migrations.go +++ b/internal/connector/internal/migrations/migrations.go @@ -28,6 +28,7 @@ var migrations = []*gormigrate.Migration{ connectorRefactor("202201310000"), addConnectorTypeCapabilitiesTable("202202040000"), addClientId("202202030000"), + addConnectorNamespaceTables("202202070000"), } func New(dbConfig *db.DatabaseConfig) (*db.Migration, func(), error) { diff --git a/internal/connector/internal/presenters/connector_namespace.go b/internal/connector/internal/presenters/connector_namespace.go new file mode 100644 index 000000000..2f2d1a90d --- /dev/null +++ b/internal/connector/internal/presenters/connector_namespace.go @@ -0,0 +1,158 @@ +package presenters + +import ( + "encoding/json" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/admin/private" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/dbapi" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/public" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/api" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/db" + "strings" +) + +func ConvertConnectorNamespaceRequest(namespaceRequest *public.ConnectorNamespaceRequest) *dbapi.ConnectorNamespace { + result := &dbapi.ConnectorNamespace{ + Model: db.Model{ + ID: api.NewID(), + }, + Name: namespaceRequest.Name, + ClusterId: namespaceRequest.ClusterId, + } + result.Annotations = make([]dbapi.ConnectorNamespaceAnnotation, len(namespaceRequest.Annotations)) + for i, annotation := range namespaceRequest.Annotations { + result.Annotations[i].NamespaceId = result.ID + result.Annotations[i].Name = annotation.Name + result.Annotations[i].Value = annotation.Value + } + + return result +} + +func ConvertConnectorNamespaceEvalRequest(namespaceRequest *public.ConnectorNamespaceEvalRequest) *dbapi.ConnectorNamespace { + result := &dbapi.ConnectorNamespace{ + Model: db.Model{ + ID: api.NewID(), + }, + Name: namespaceRequest.Name, + } + result.Annotations = make([]dbapi.ConnectorNamespaceAnnotation, len(namespaceRequest.Annotations)) + for i, annotation := range namespaceRequest.Annotations { + result.Annotations[i].NamespaceId = result.ID + result.Annotations[i].Name = annotation.Name + result.Annotations[i].Value = annotation.Value + } + + return result +} + +func ConvertConnectorNamespaceWithTenantRequest(namespaceRequest *private.ConnectorNamespaceWithTenantRequest) *dbapi.ConnectorNamespace { + result := &dbapi.ConnectorNamespace{ + Model: db.Model{ + ID: api.NewID(), + }, + Name: namespaceRequest.Name, + ClusterId: namespaceRequest.ClusterId, + } + if namespaceRequest.Tenant.UserId != "" { + result.TenantUserId = &namespaceRequest.Tenant.UserId + result.TenantUser = &dbapi.ConnectorTenantUser{ + Model: db.Model{ + ID: *result.TenantUserId, + }, + } + } + if namespaceRequest.Tenant.OrganisationId != "" { + result.TenantOrganisationId = &namespaceRequest.Tenant.OrganisationId + result.TenantOrganisation = &dbapi.ConnectorTenantOrganisation{ + Model: db.Model{ + ID: *result.TenantOrganisationId, + }, + } + } + result.Annotations = make([]dbapi.ConnectorNamespaceAnnotation, len(namespaceRequest.Annotations)) + for i, annotation := range namespaceRequest.Annotations { + result.Annotations[i].NamespaceId = result.ID + result.Annotations[i].Name = annotation.Name + result.Annotations[i].Value = annotation.Value + } + + return result +} + +func PresentConnectorNamespace(namespace *dbapi.ConnectorNamespace) public.ConnectorNamespace { + annotations := make([]public.ConnectorNamespaceRequestMetaAnnotations, len(namespace.Annotations)) + for i, anno := range namespace.Annotations { + annotations[i].Name = anno.Name + annotations[i].Value = anno.Value + } + + reference := PresentReference(namespace.ID, namespace) + result := public.ConnectorNamespace{ + Id: reference.Id, + Kind: reference.Kind, + Href: reference.Href, + + CreatedAt: namespace.CreatedAt, + ModifiedAt: namespace.UpdatedAt, + Owner: namespace.Owner, + Version: namespace.Version, + + Name: namespace.Name, + ClusterId: namespace.ClusterId, + Tenant: public.ConnectorNamespaceTenant{}, + Annotations: annotations, + } + if namespace.TenantUserId != nil { + result.Tenant.Kind = "user" + result.Tenant.UserId = *namespace.TenantUserId + } + if namespace.TenantOrganisationId != nil { + result.Tenant.Kind = "organisation" + result.Tenant.OrganisationId = *namespace.TenantOrganisationId + } + if namespace.Expiration != nil { + bytes, _ := json.Marshal(namespace.Expiration) + result.Expiration = strings.Trim(string(bytes), "\"") + } + + return result +} + +func PresentPrivateConnectorNamespace(namespace *dbapi.ConnectorNamespace) private.ConnectorNamespace { + annotations := make([]private.ConnectorNamespaceRequestMetaAnnotations, len(namespace.Annotations)) + for i, anno := range namespace.Annotations { + annotations[i].Name = anno.Name + annotations[i].Value = anno.Value + } + + reference := PresentReference(namespace.ID, namespace) + result := private.ConnectorNamespace{ + Id: reference.Id, + Kind: reference.Kind, + Href: reference.Href, + + CreatedAt: namespace.CreatedAt, + ModifiedAt: namespace.UpdatedAt, + Owner: namespace.Owner, + Version: namespace.Version, + + Name: namespace.Name, + ClusterId: namespace.ClusterId, + Tenant: private.ConnectorNamespaceTenant{}, + Annotations: annotations, + } + if namespace.TenantUserId != nil { + result.Tenant.Kind = "user" + result.Tenant.UserId = *namespace.TenantUserId + } + if namespace.TenantOrganisationId != nil { + result.Tenant.Kind = "organisation" + result.Tenant.OrganisationId = *namespace.TenantOrganisationId + } + if namespace.Expiration != nil { + bytes, _ := json.Marshal(namespace.Expiration) + result.Expiration = string(bytes) + } + + return result +} diff --git a/internal/connector/internal/presenters/object_reference.go b/internal/connector/internal/presenters/object_reference.go index 174ac4038..e759f9a22 100644 --- a/internal/connector/internal/presenters/object_reference.go +++ b/internal/connector/internal/presenters/object_reference.go @@ -15,6 +15,8 @@ const ( KindConnectorCluster = "ConnectorCluster" // KindConnectorDeployment is a string identifier for the type dbapi.ConnectorDeployment KindConnectorDeployment = "ConnectorDeployment" + // KindConnectorNamespace is a string identifier for the type dbapi.ConnectorNamespace + KindConnectorNamespace = "ConnectorNamespace" // KindConnectorType is a string identifier for the type dbapi.ConnectorType KindConnectorType = "ConnectorType" // KindError is a string identifier for the type api.ServiceError @@ -33,6 +35,8 @@ func objectKind(i interface{}) string { return KindConnectorCluster case dbapi.ConnectorDeployment, *dbapi.ConnectorDeployment: return KindConnectorDeployment + case dbapi.ConnectorNamespace, *dbapi.ConnectorNamespace: + return KindConnectorNamespace case dbapi.ConnectorType, *dbapi.ConnectorType: return KindConnectorType case errors.ServiceError, *errors.ServiceError: @@ -54,6 +58,8 @@ func objectPath(id string, obj interface{}) string { return fmt.Sprintf("/api/connector_mgmt/v1/kafka_connector_clusters/%s/deployments/%s", obj.ClusterID, id) case *dbapi.ConnectorDeployment: return fmt.Sprintf("/api/connector_mgmt/v1/kafka_connector_clusters/%s/deployments/%s", obj.ClusterID, id) + case dbapi.ConnectorNamespace, *dbapi.ConnectorNamespace: + return fmt.Sprintf("/api/connector_mgmt/v1/kafka_connector_namespaces/%s", id) default: return "" } diff --git a/internal/connector/internal/routes/route_loader.go b/internal/connector/internal/routes/route_loader.go index 0a8c9f284..e2462d4eb 100644 --- a/internal/connector/internal/routes/route_loader.go +++ b/internal/connector/internal/routes/route_loader.go @@ -24,17 +24,18 @@ import ( type options struct { di.Inject - ConnectorsConfig *config.ConnectorsConfig - ServerConfig *server.ServerConfig - ErrorsHandler *coreHandlers.ErrorHandler - AuthorizeMiddleware *acl.AccessControlListMiddleware - KeycloakService services.KafkaKeycloakService - AuthAgentService auth.AuthAgentService - ConnectorAdminHandler *handlers.ConnectorAdminHandler - ConnectorTypesHandler *handlers.ConnectorTypesHandler - ConnectorsHandler *handlers.ConnectorsHandler - ConnectorClusterHandler *handlers.ConnectorClusterHandler - DB *db.ConnectionFactory + ConnectorsConfig *config.ConnectorsConfig + ServerConfig *server.ServerConfig + ErrorsHandler *coreHandlers.ErrorHandler + AuthorizeMiddleware *acl.AccessControlListMiddleware + KeycloakService services.KafkaKeycloakService + AuthAgentService auth.AuthAgentService + ConnectorAdminHandler *handlers.ConnectorAdminHandler + ConnectorTypesHandler *handlers.ConnectorTypesHandler + ConnectorsHandler *handlers.ConnectorsHandler + ConnectorClusterHandler *handlers.ConnectorClusterHandler + ConnectorNamespaceHandler *handlers.ConnectorNamespaceHandler + DB *db.ConnectionFactory } func NewRouteLoader(s options) environments.RouteLoader { @@ -111,6 +112,21 @@ func (s *options) AddRoutes(mainRouter *mux.Router) error { apiV1ConnectorClustersRouter.HandleFunc("/{connector_cluster_id}/{_:addon[-_]parameters}", s.ConnectorClusterHandler.GetAddonParameters).Methods(http.MethodGet) apiV1ConnectorClustersRouter.Use(s.AuthorizeMiddleware.Authorize) + // /api/connector_mgmt/v1/kafka_connector_namespaces + v1Collections = append(v1Collections, api.CollectionMetadata{ + ID: "kafka_connector_namespaces", + Kind: "ConnectorNamespaceList", + }) + + apiV1ConnectorNamespacesRouter := apiV1Router.PathPrefix("/{_:kafka[-_]connector[-_]namespaces}").Subrouter() + apiV1ConnectorNamespacesRouter.HandleFunc("", s.ConnectorNamespaceHandler.Create).Methods(http.MethodPost) + apiV1ConnectorNamespacesRouter.HandleFunc("/eval", s.ConnectorNamespaceHandler.CreateEvaluation).Methods(http.MethodPost) + apiV1ConnectorNamespacesRouter.HandleFunc("", s.ConnectorNamespaceHandler.List).Methods(http.MethodGet) + apiV1ConnectorNamespacesRouter.HandleFunc("/{connector_namespace_id}", s.ConnectorNamespaceHandler.Get).Methods(http.MethodGet) + apiV1ConnectorNamespacesRouter.HandleFunc("/{connector_namespace_id}", s.ConnectorNamespaceHandler.Update).Methods(http.MethodPut) + apiV1ConnectorNamespacesRouter.HandleFunc("/{connector_namespace_id}", s.ConnectorNamespaceHandler.Delete).Methods(http.MethodDelete) + apiV1ConnectorNamespacesRouter.Use(s.AuthorizeMiddleware.Authorize) + // This section adds the API's accessed by the connector agent... { // /api/connector_mgmt/v1/kafka_connector_clusters/{id} @@ -123,19 +139,24 @@ func (s *options) AddRoutes(mainRouter *mux.Router) error { } // This section adds APIs accessed by connector admins - adminRouter := apiV1Router.PathPrefix("/admin/{_:kafka[-_]connector[-_]clusters}").Subrouter() + adminRouter := apiV1Router.PathPrefix("/admin").Subrouter() rolesMapping := map[string][]string{ - http.MethodGet: {auth.ConnectorFleetManagerAdminReadRole, auth.ConnectorFleetManagerAdminWriteRole, auth.ConnectorFleetManagerAdminFullRole}, - http.MethodPut: {auth.ConnectorFleetManagerAdminWriteRole, auth.ConnectorFleetManagerAdminFullRole}, + http.MethodDelete: {auth.ConnectorFleetManagerAdminWriteRole, auth.ConnectorFleetManagerAdminFullRole}, + http.MethodGet: {auth.ConnectorFleetManagerAdminReadRole, auth.ConnectorFleetManagerAdminWriteRole, auth.ConnectorFleetManagerAdminFullRole}, + http.MethodPost: {auth.ConnectorFleetManagerAdminWriteRole, auth.ConnectorFleetManagerAdminFullRole}, + http.MethodPut: {auth.ConnectorFleetManagerAdminWriteRole, auth.ConnectorFleetManagerAdminFullRole}, } adminRouter.Use(auth.NewRequireIssuerMiddleware().RequireIssuer([]string{s.KeycloakService.GetConfig().OSDClusterIDPRealm.ValidIssuerURI}, kerrors.ErrorNotFound)) adminRouter.Use(auth.NewRolesAuhzMiddleware().RequireRolesForMethods(rolesMapping, kerrors.ErrorNotFound)) adminRouter.Use(auth.NewAuditLogMiddleware().AuditLog(kerrors.ErrorNotFound)) - adminRouter.HandleFunc("", s.ConnectorAdminHandler.ListConnectorClusters).Methods(http.MethodGet) - adminRouter.HandleFunc("/{connector_cluster_id}/upgrades/type", s.ConnectorAdminHandler.GetConnectorUpgradesByType).Methods(http.MethodGet) - adminRouter.HandleFunc("/{connector_cluster_id}/upgrades/type", s.ConnectorAdminHandler.UpgradeConnectorsByType).Methods(http.MethodPut) - adminRouter.HandleFunc("/{connector_cluster_id}/upgrades/operator", s.ConnectorAdminHandler.GetConnectorUpgradesByOperator).Methods(http.MethodGet) - adminRouter.HandleFunc("/{connector_cluster_id}/upgrades/operator", s.ConnectorAdminHandler.UpgradeConnectorsByOperator).Methods(http.MethodPut) + adminRouter.HandleFunc("/{_:kafka[-_]connector[-_]clusters}", s.ConnectorAdminHandler.ListConnectorClusters).Methods(http.MethodGet) + adminRouter.HandleFunc("/{_:kafka[-_]connector[-_]clusters}/{connector_cluster_id}/upgrades/type", s.ConnectorAdminHandler.GetConnectorUpgradesByType).Methods(http.MethodGet) + adminRouter.HandleFunc("/{_:kafka[-_]connector[-_]clusters}/{connector_cluster_id}/upgrades/type", s.ConnectorAdminHandler.UpgradeConnectorsByType).Methods(http.MethodPut) + adminRouter.HandleFunc("/{_:kafka[-_]connector[-_]clusters}/{connector_cluster_id}/upgrades/operator", s.ConnectorAdminHandler.GetConnectorUpgradesByOperator).Methods(http.MethodGet) + adminRouter.HandleFunc("/{_:kafka[-_]connector[-_]clusters}/{connector_cluster_id}/upgrades/operator", s.ConnectorAdminHandler.UpgradeConnectorsByOperator).Methods(http.MethodPut) + adminRouter.HandleFunc("/{_:kafka[-_]connector[-_]namespaces}", s.ConnectorAdminHandler.GetConnectorNamespaces).Methods(http.MethodGet) + adminRouter.HandleFunc("/{_:kafka[-_]connector[-_]namespaces}", s.ConnectorAdminHandler.CreateConnectorNamespace).Methods(http.MethodPost) + adminRouter.HandleFunc("/{_:kafka[-_]connector[-_]namespaces}/{namespace_id}", s.ConnectorAdminHandler.DeleteConnectorNamespace).Methods(http.MethodDelete) v1Metadata := api.VersionMetadata{ ID: "v1", diff --git a/internal/connector/internal/services/namespace_service.go b/internal/connector/internal/services/namespace_service.go new file mode 100644 index 000000000..4e6102feb --- /dev/null +++ b/internal/connector/internal/services/namespace_service.go @@ -0,0 +1,241 @@ +package services + +import ( + "context" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/dbapi" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/config" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/api" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/db" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/errors" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/services" + coreServices "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/services/queryparser" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/services/signalbus" + "math/rand" + "time" +) + +type ConnectorNamespaceService interface { + Create(ctx context.Context, request *dbapi.ConnectorNamespace) *errors.ServiceError + Update(ctx context.Context, request *dbapi.ConnectorNamespace) *errors.ServiceError + Get(ctx context.Context, namespaceID string) (*dbapi.ConnectorNamespace, *errors.ServiceError) + List(ctx context.Context, clusterIDs []string, listArguments *services.ListArguments) (dbapi.ConnectorNamespaceList, *api.PagingMeta, *errors.ServiceError) + Delete(ctx context.Context, namespaceName string) *errors.ServiceError + SetTenantClusterId(request *dbapi.ConnectorNamespace, organisationId string) *errors.ServiceError + SetEvalClusterId(request *dbapi.ConnectorNamespace) *errors.ServiceError + GetOwnerClusterIds(userId string, ownerClusterIds *[]string) *errors.ServiceError + GetOrgClusterIds(userId string, organisationId string, orgClusterIds *[]string) *errors.ServiceError +} + +var _ ConnectorNamespaceService = &connectorNamespaceService{} + +type connectorNamespaceService struct { + connectionFactory *db.ConnectionFactory + connectorsConfig *config.ConnectorsConfig + bus signalbus.SignalBus +} + +func init() { + // random seed + rand.Seed(time.Now().UnixNano()) +} + +func NewConnectorNamespaceService(factory *db.ConnectionFactory, config *config.ConnectorsConfig, bus signalbus.SignalBus) *connectorNamespaceService { + return &connectorNamespaceService{ + connectionFactory: factory, + connectorsConfig: config, + bus: bus, + } +} + +func (k *connectorNamespaceService) SetTenantClusterId(request *dbapi.ConnectorNamespace, organisationId string) *errors.ServiceError { + userID := request.Owner + + // get owned clusters + var ownerClusterIds []string + if err := k.GetOwnerClusterIds(userID, &ownerClusterIds); err != nil { + return err + } + + numOwnerClusters := len(ownerClusterIds) + if numOwnerClusters == 0 { + + // get org clusters + var orgClusterIds []string + if err := k.GetOrgClusterIds(userID, organisationId, &orgClusterIds); err != nil { + return err + } + + numOrgClusters := len(orgClusterIds) + if numOrgClusters == 0 { + return errors.Unauthorized("no clusters for organisation %v", organisationId) + } else if numOrgClusters == 1 { + request.ClusterId = orgClusterIds[0] + } else { + // TODO add support for load balancing strategies + // pick a cluster at random + request.ClusterId = orgClusterIds[rand.Intn(numOrgClusters)] + } + + } else if numOwnerClusters == 1 { + request.ClusterId = ownerClusterIds[0] + } else { + // TODO add support for load balancing strategies + // pick a cluster at random + request.ClusterId = ownerClusterIds[rand.Intn(numOwnerClusters)] + } + + return nil +} + +func (k *connectorNamespaceService) GetOrgClusterIds(userId, organisationId string, orgClusterIds *[]string) *errors.ServiceError { + dbConn := k.connectionFactory.New() + if err := dbConn.Raw("SELECT id FROM connector_clusters WHERE deleted_at is null AND owner <> ? AND organisation_id = ?", userId, organisationId). + Scan(orgClusterIds).Error; err != nil { + return errors.Unauthorized("failed to get cluster id for organisation %v: %v", organisationId, err) + } + return nil +} + +func (k *connectorNamespaceService) GetOwnerClusterIds(userId string, ownerClusterIds *[]string) *errors.ServiceError { + dbConn := k.connectionFactory.New() + if err := dbConn.Raw("SELECT id FROM connector_clusters WHERE deleted_at is null AND owner = ?", userId). + Scan(ownerClusterIds).Error; err != nil { + return errors.Unauthorized("failed to get cluster id for user %v: %v", userId, err) + } + return nil +} + +func (k *connectorNamespaceService) SetEvalClusterId(request *dbapi.ConnectorNamespace) *errors.ServiceError { + var availableClusters []string + + // get eval clusters + dbConn := k.connectionFactory.New() + if err := dbConn.Raw("SELECT id FROM connector_clusters WHERE deleted_at is null AND organisation_id IN ?", + k.connectorsConfig.ConnectorEvalOrganizations). + Scan(&availableClusters).Error; err != nil { + return errors.Unauthorized("failed to get eval cluster id: %v", err) + } + + numOrgClusters := len(availableClusters) + if numOrgClusters == 0 { + return errors.Unauthorized("no eval clusters") + } else if numOrgClusters == 1 { + request.ClusterId = availableClusters[0] + } else { + // TODO add support for load balancing strategies + // pick a cluster at random + request.ClusterId = availableClusters[rand.Intn(numOrgClusters)] + } + + // also set expiration duration + expiry := time.Now().Add(k.connectorsConfig.ConnectorEvalDuration) + request.Expiration = &expiry + return nil +} + +func (k *connectorNamespaceService) Create(ctx context.Context, request *dbapi.ConnectorNamespace) *errors.ServiceError { + + dbConn := k.connectionFactory.New() + if err := dbConn.Create(request).Error; err != nil { + return errors.GeneralError("failed to create connector namespace: %v", err) + } + // reload namespace to get version update + if err := dbConn.Find(request).Error; err != nil { + return errors.GeneralError("failed to read new connector namespace: %v", err) + } + + // TODO: increment connector namespace metrics + // metrics.IncreaseStatusCountMetric(constants.KafkaRequestStatusAccepted.String()) + return nil +} + +func (k *connectorNamespaceService) Update(ctx context.Context, request *dbapi.ConnectorNamespace) *errors.ServiceError { + + dbConn := k.connectionFactory.New() + if err := dbConn.Save(request).Error; err != nil { + return errors.GeneralError("failed to update connector namespace: %v", err) + } + + // TODO use signal bus and a namespace worker to sync connectors in updated namespace + + // TODO: increment connector namespace metrics + // metrics.IncreaseStatusCountMetric(constants.KafkaRequestStatusAccepted.String()) + return nil +} + +func (k *connectorNamespaceService) Get(ctx context.Context, namespaceID string) (*dbapi.ConnectorNamespace, *errors.ServiceError) { + dbConn := k.connectionFactory.New() + result := &dbapi.ConnectorNamespace{ + Model: db.Model{ + ID: namespaceID, + }, + } + if err := dbConn.Find(result).Error; err != nil { + return nil, errors.GeneralError("failed to get connector namespace: %v", err) + } + + // TODO: increment connector namespace metrics + // metrics.IncreaseStatusCountMetric(constants.KafkaRequestStatusAccepted.String()) + return result, nil +} + +var validNamespaceColumns = []string{"name", "cluster_id", "owner", "expiration", "tenant_user_id", "tenant_organisation_id"} + +func (k *connectorNamespaceService) List(ctx context.Context, clusterIDs []string, + listArguments *services.ListArguments) (dbapi.ConnectorNamespaceList, *api.PagingMeta, *errors.ServiceError) { + var resourceList dbapi.ConnectorNamespaceList + pagingMeta := api.PagingMeta{ + Page: listArguments.Page, + Size: listArguments.Size, + Total: 0, + } + dbConn := k.connectionFactory.New().Model(&resourceList) + if len(clusterIDs) != 0 { + dbConn = dbConn.Where("cluster_id IN ?", clusterIDs) + } + + // Apply search query + if len(listArguments.Search) > 0 { + queryParser := coreServices.NewQueryParser(validNamespaceColumns...) + searchDbQuery, err := queryParser.Parse(listArguments.Search) + if err != nil { + return resourceList, &pagingMeta, errors.NewWithCause(errors.ErrorFailedToParseSearch, err, "Unable to list connector namespace requests: %s", err.Error()) + } + dbConn = dbConn.Where(searchDbQuery.Query, searchDbQuery.Values...) + } + + // set total, limit and paging (based on https://gitlab.cee.redhat.com/service/api-guidelines#user-content-paging) + total := int64(pagingMeta.Total) + dbConn.Count(&total) + pagingMeta.Total = int(total) + if pagingMeta.Size > pagingMeta.Total { + pagingMeta.Size = pagingMeta.Total + } + dbConn = dbConn.Offset((pagingMeta.Page - 1) * pagingMeta.Size).Limit(pagingMeta.Size) + + // execute query + result := dbConn. + Preload("Annotations"). + Preload("TenantUser"). + Preload("TenantOrganisation"). + Find(&resourceList) + if result.Error != nil { + return nil, nil, errors.GeneralError("failed to get connector namespaces: %v", result.Error) + } + + // TODO: increment connector namespace metrics + // metrics.IncreaseStatusCountMetric(constants.KafkaRequestStatusAccepted.String()) + return resourceList, &pagingMeta, nil +} + +func (k *connectorNamespaceService) Delete(ctx context.Context, namespaceId string) *errors.ServiceError { + dbConn := k.connectionFactory.New() + dbConn = dbConn.Where("id = ?", namespaceId) + if err := dbConn.Delete(&dbapi.ConnectorNamespace{}).Error; err != nil { + return errors.GeneralError("failed to delete connector namespace: %v", err) + } + + // TODO: increment connector namespace metrics + // metrics.IncreaseStatusCountMetric(constants.KafkaRequestStatusAccepted.String()) + return nil +} diff --git a/internal/connector/providers.go b/internal/connector/providers.go index 117597014..079c7f037 100644 --- a/internal/connector/providers.go +++ b/internal/connector/providers.go @@ -21,7 +21,7 @@ import ( func ConfigProviders(kafkaEnabled bool) di.Option { result := di.Options( - di.Provide(config.NewConnectorsConfig, di.As(new(environments2.ConfigModule))), + di.Provide(config.NewConnectorsConfig, di.As(new(environments2.ConfigModule)), di.As(new(environments2.ServiceValidator))), di.Provide(environments2.Func(serviceProviders)), di.Provide(migrations.New), di.Provide(cmdvault.NewVaultCommand), @@ -51,6 +51,8 @@ func serviceProviders() di.Option { di.Provide(services.NewConnectorTypesService, di.As(new(services.ConnectorTypesService))), di.Provide(services.NewConnectorClusterService, di.As(new(services.ConnectorClusterService))), di.Provide(services.NewConnectorClusterService, di.As(new(auth.AuthAgentService))), + di.Provide(services.NewConnectorNamespaceService, di.As(new(services.ConnectorNamespaceService))), + di.Provide(handlers.NewConnectorNamespaceHandler), di.Provide(handlers.NewConnectorAdminHandler), di.Provide(handlers.NewConnectorTypesHandler), di.Provide(handlers.NewConnectorsHandler), diff --git a/internal/connector/test/integration/feature_test.go b/internal/connector/test/integration/feature_test.go index 07cee6441..4a3889c75 100644 --- a/internal/connector/test/integration/feature_test.go +++ b/internal/connector/test/integration/feature_test.go @@ -23,6 +23,8 @@ func TestMain(m *testing.M) { h, teardown := test.NewHelperWithHooks(t, ocmServer, func(c *config.ConnectorsConfig, kc *keycloak.KeycloakConfig) { c.ConnectorCatalogDirs = []string{"./internal/connector/test/integration/connector-catalog"} + c.ConnectorEvalDurationString = "30m" + c.ConnectorEvalOrganizations = []string{"13640210"} kc.KeycloakClientExpire = true }, diff --git a/internal/connector/test/integration/features/connector-agent-api.feature b/internal/connector/test/integration/features/connector-agent-api.feature index 60a9dbdf6..71e5febaa 100644 --- a/internal/connector/test/integration/features/connector-agent-api.feature +++ b/internal/connector/test/integration/features/connector-agent-api.feature @@ -616,7 +616,7 @@ Feature: connector agent API # Now lets verify connector upgrades due to catalog updates Given I am logged in as "Ricky Bobby" - And I GET path "/v1/admin/kafka_connector_clusters/" + And I GET path "/v1/admin/kafka_connector_clusters" And the response code should be 200 And the ".items[0]" selection from the response should match json: """ @@ -625,7 +625,7 @@ Feature: connector agent API "id": "${response.items[0].id}", "kind": "ConnectorCluster", "created_at": "${response.items[0].created_at}", - "name": "New Cluster", + "name": "${response.items[0].name}", "owner": "${response.items[0].owner}", "modified_at": "${response.items[0].modified_at}", "status": { diff --git a/internal/connector/test/integration/features/connector-api.feature b/internal/connector/test/integration/features/connector-api.feature index c6b77a985..7c522b38d 100644 --- a/internal/connector/test/integration/features/connector-api.feature +++ b/internal/connector/test/integration/features/connector-api.feature @@ -1449,6 +1449,11 @@ Feature: create a connector "href": "/api/connector_mgmt/v1/kafka_connector_clusters", "id": "kafka_connector_clusters", "kind": "ConnectorClusterList" + }, + { + "href": "/api/connector_mgmt/v1/kafka_connector_namespaces", + "id": "kafka_connector_namespaces", + "kind": "ConnectorNamespaceList" } ] } @@ -1477,6 +1482,11 @@ Feature: create a connector "href": "/api/connector_mgmt/v1/kafka_connector_clusters", "id": "kafka_connector_clusters", "kind": "ConnectorClusterList" + }, + { + "href": "/api/connector_mgmt/v1/kafka_connector_namespaces", + "id": "kafka_connector_namespaces", + "kind": "ConnectorNamespaceList" } ] } diff --git a/internal/connector/test/integration/features/connector-multitenancy-api.feature b/internal/connector/test/integration/features/connector-multitenancy-api.feature new file mode 100644 index 000000000..94a84d333 --- /dev/null +++ b/internal/connector/test/integration/features/connector-multitenancy-api.feature @@ -0,0 +1,499 @@ +Feature: connector namespaces API + In order to deploy connectors to an addon OSD cluster + As a regular user, I need to be able to create and manage namespaces in both evaluation and non-evaluation clusters + As an admin user, I need to be able to create and manage namespaces for other users and clusters + + Background: + Given the path prefix is "/api/connector_mgmt" + + # User for eval organization id 13640210 configured in internal/connector/test/integration/feature_test.go:27 + Given a user named "Gru" in organization "13640210" + + # eval users used in public API + Given a user named "Stuart" in organization "13640210" + Given I store userid for "Stuart" as ${stuart_user_id} + Given a user named "Kevin" in organization "13640211" + Given I store userid for "Kevin" as ${kevin_user_id} + Given a user named "Bob" in organization "13640212" + Given I store userid for "Bob" as ${bob_user_id} + + # eval users used in admin API + Given a user named "Dave" in organization "13640213" + Given I store userid for "Dave" as ${dave_user_id} + Given a user named "Phil" in organization "13640214" + Given I store userid for "Phil" as ${phil_user_id} + Given a user named "Tim" in organization "13640215" + Given I store userid for "Tim" as ${tim_user_id} + + # users in organisation 13640220 + Given a user named "Dusty" in organization "13640220" + Given I store userid for "Dusty" as ${dusty_user_id} + Given a user named "Lucky" in organization "13640220" + Given I store userid for "Lucky" as ${lucky_user_id} + Given a user named "Ned" in organization "13640220" + Given I store userid for "Ned" as ${ned_user_id} + + # users in organisation 13640221 + Given a user named "El Guapo" in organization "13640221" + Given I store userid for "El Guapo" as ${guapo_user_id} + + Scenario Outline: Create eval namespace + Given I am logged in as "Gru" + When I POST path "/v1/kafka_connector_clusters" with json body: + """ + { + "name": "Evaluation Cluster" + } + """ + Then the response code should be 202 + And the ".status.state" selection from the response should match "disconnected" + + Given I store the ".id" selection from the response as ${connector_cluster_id} + When I GET path "/v1/kafka_connector_clusters/${connector_cluster_id}/addon_parameters" + Then the response code should be 200 + And get and store access token using the addon parameter response as ${shard_token} and clientID as ${clientID} + And I remember keycloak client for cleanup with clientID: ${clientID} + + # There should be no namespaces at first for user + Given I am logged in as "" + When I GET path "/v1/kafka_connector_namespaces/" + Then the response code should be 200 + And the response should match json: + """ + { + "items": [], + "kind": "ConnectorNamespaceList", + "page": 1, + "size": 0, + "total": 0 + } + """ + + # Create an eval namespace + Given I am logged in as "" + When I POST path "/v1/kafka_connector_namespaces/eval" with json body: + """ + { + "name": "_namespace", + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ] + } + """ + Then the response code should be 201 + And the response should match json: + """ + { + "id": "${response.id}", + "kind": "ConnectorNamespace", + "href": "/api/connector_mgmt/v1/kafka_connector_namespaces/${response.id}", + "name": "_namespace", + "owner": "${}", + "version": ${response.version}, + "cluster_id": "${response.cluster_id}", + "created_at": "${response.created_at}", + "modified_at": "${response.modified_at}", + "expiration": "${response.expiration}", + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + "tenant": { + "kind": "user", + "user_id": "${}" + } + } + """ + And I store the ".id" selection from the response as ${namespace_id} + + # Delete eval namespace + Given I am logged in as "" + When I DELETE path "/v1/kafka_connector_namespaces/${namespace_id}" + Then the response code should be 204 + And I GET path "/v1/kafka_connector_namespaces" + And the response code should be 200 + And the response should match json: + """ + { + "items": [], + "kind": "ConnectorNamespaceList", + "page": 1, + "size": 0, + "total": 0 + } + """ + + # cleanup eval cluster + Given I am logged in as "Gru" + When I DELETE path "/v1/kafka_connector_clusters/${connector_cluster_id}" + Then the response code should be 204 + And the response should match "" + + Examples: + | user | user_id | + | Stuart | stuart_user_id | + | Kevin | kevin_user_id | + | Bob | bob_user_id | + + Scenario: Create namespaces in cluster for organization 13640220 + Given I am logged in as "Dusty" + When I POST path "/v1/kafka_connector_clusters" with json body: + """ + { + "name": "Dusty's Cluster" + } + """ + Then the response code should be 202 + And the ".status.state" selection from the response should match "disconnected" + + Given I store the ".id" selection from the response as ${connector_cluster_id} + When I GET path "/v1/kafka_connector_clusters/${connector_cluster_id}/addon_parameters" + Then the response code should be 200 + And get and store access token using the addon parameter response as ${shard_token} and clientID as ${clientID} + And I remember keycloak client for cleanup with clientID: ${clientID} + + # There should be no namespaces at first for organization 13640220 + Given I am logged in as "Lucky" + When I GET path "/v1/kafka_connector_namespaces" + Then the response code should be 200 + And the response should match json: + """ + { + "items": [], + "kind": "ConnectorNamespaceList", + "page": 1, + "size": 0, + "total": 0 + } + """ + + # Create a namespace + Given I am logged in as "Lucky" + When I POST path "/v1/kafka_connector_namespaces/" with json body: + """ + { + "name": "shared_namespace", + "cluster_id": "${connector_cluster_id}", + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ] + } + """ + Then the response code should be 201 + And the response should match json: + """ + { + "id": "${response.id}", + "kind": "ConnectorNamespace", + "href": "/api/connector_mgmt/v1/kafka_connector_namespaces/${response.id}", + "name": "shared_namespace", + "owner": "${lucky_user_id}", + "version": ${response.version}, + "cluster_id": "${connector_cluster_id}", + "created_at": "${response.created_at}", + "modified_at": "${response.modified_at}", + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + "tenant": { + "kind": "organisation", + "organisation_id": "13640220" + } + } + """ + And I store the ".id" selection from the response as ${namespace_id} + + # All organization members MUST be able to see the org tenant namespace + Given I am logged in as "Ned" + When I GET path "/v1/kafka_connector_namespaces/" + Then the response code should be 200 + And the response should match json: + """ + { + "items": [ + { + "id": "${namespace_id}", + "kind": "ConnectorNamespace", + "href": "/api/connector_mgmt/v1/kafka_connector_namespaces/${namespace_id}", + "name": "shared_namespace", + "owner": "${lucky_user_id}", + "version": ${response.items[0].version}, + "cluster_id": "${connector_cluster_id}", + "created_at": "${response.items[0].created_at}", + "modified_at": "${response.items[0].modified_at}", + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + "tenant": { + "kind": "organisation", + "organisation_id": "13640220" + } + } + ], + "kind": "ConnectorNamespaceList", + "page": 1, + "size": 1, + "total": 1 + } + """ + + # Delete namespace + Given I am logged in as "Lucky" + When I DELETE path "/v1/kafka_connector_namespaces/${namespace_id}" + Then the response code should be 204 + And I GET path "/v1/kafka_connector_namespaces" + And the response code should be 200 + And the response should match json: + """ + { + "items": [], + "kind": "ConnectorNamespaceList", + "page": 1, + "size": 0, + "total": 0 + } + """ + + # cleanup cluster + Given I am logged in as "Dusty" + When I DELETE path "/v1/kafka_connector_clusters/${connector_cluster_id}" + Then the response code should be 204 + And the response should match "" + + Scenario Outline: Use Admin API to create namespaces for end users + Given I am logged in as "" + + #----------------------------------------------------------------------------------- + # Create a target cluster, and get the shard access token. + # ----------------------------------------------------------------------------------- + When I POST path "/v1/kafka_connector_clusters" with json body: + """ + { + "name": "User 's Cluster" + } + """ + Then the response code should be 202 + And the ".status.state" selection from the response should match "disconnected" + + Given I store the ".id" selection from the response as ${connector_cluster_id} + When I GET path "/v1/kafka_connector_clusters/${connector_cluster_id}/addon_parameters" + Then the response code should be 200 + And get and store access token using the addon parameter response as ${shard_token} and clientID as ${clientID} + And I remember keycloak client for cleanup with clientID: ${clientID} + + #----------------------------------------------------------------------------------------------------------------- + # In this part of the Scenario we create connector namespaces + #----------------------------------------------------------------------------------------------------------------- + + # There should be no namespaces first + Given I am logged in as "Ricky Bobby" + When I GET path "/v1/admin/kafka_connector_namespaces/?search=cluster_id=${connector_cluster_id}" + Then the response code should be 200 + And the response should match json: + """ + { + "items": [], + "kind": "ConnectorNamespaceList", + "page": 1, + "size": 0, + "total": 0 + } + """ + + # Create a namespace + Given I am logged in as "Ricky Bobby" + When I POST path "/v1/admin/kafka_connector_namespaces/" with json body: + """ + { + "name": "_namespace", + "cluster_id": "${connector_cluster_id}", + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + "tenant": { + "kind": "user", + "user_id": "${}" + } + } + """ + Then the response code should be 201 + And the response should match json: + """ + { + "id": "${response.id}", + "kind": "ConnectorNamespace", + "href": "/api/connector_mgmt/v1/kafka_connector_namespaces/${response.id}", + "name": "_namespace", + "owner": "${}", + "version": ${response.version}, + "cluster_id": "${connector_cluster_id}", + "created_at": "${response.created_at}", + "modified_at": "${response.modified_at}", + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + "tenant": { + "kind": "user", + "user_id": "${}" + } + } + """ + And I store the ".id" selection from the response as ${namespace_id} + + # Delete namespace + Given I am logged in as "Ricky Bobby" + When I DELETE path "/v1/admin/kafka_connector_namespaces/${namespace_id}" + Then the response code should be 204 + And I GET path "/v1/admin/kafka_connector_namespaces?search=cluster_id=${connector_cluster_id}" + And the response code should be 200 + And the response should match json: + """ + { + "items": [], + "kind": "ConnectorNamespaceList", + "page": 1, + "size": 0, + "total": 0 + } + """ + + #cleanup + Given I am logged in as "" + When I DELETE path "/v1/kafka_connector_clusters/${connector_cluster_id}" + Then the response code should be 204 + And the response should match "" + + Examples: + | user | user_id | + | Dave | dave_user_id | + | Phil | phil_user_id | + | Tim | tim_user_id | + + Scenario: Use Admin API to create namespace for organization 13640221. + Given I am logged in as "El Guapo" + + #----------------------------------------------------------------------------------- + # Create a target cluster, and get the shard access token. + # ----------------------------------------------------------------------------------- + When I POST path "/v1/kafka_connector_clusters" with json body: + """ + { + "name": "El Guapo's Cluster" + } + """ + Then the response code should be 202 + And the ".status.state" selection from the response should match "disconnected" + + Given I store the ".id" selection from the response as ${connector_cluster_id} + When I GET path "/v1/kafka_connector_clusters/${connector_cluster_id}/addon_parameters" + Then the response code should be 200 + And get and store access token using the addon parameter response as ${shard_token} and clientID as ${clientID} + And I remember keycloak client for cleanup with clientID: ${clientID} + + #----------------------------------------------------------------------------------------------------------------- + # In this part of the Scenario we create connector namespace + #----------------------------------------------------------------------------------------------------------------- + + # There should be no namespaces first in cluster ${connector_cluster_id} + Given I am logged in as "Ricky Bobby" + When I GET path "/v1/admin/kafka_connector_namespaces/?search=cluster_id=${connector_cluster_id}" + Then the response code should be 200 + And the response should match json: + """ + { + "items": [], + "kind": "ConnectorNamespaceList", + "page": 1, + "size": 0, + "total": 0 + } + """ + + # Create a namespace + Given I am logged in as "Ricky Bobby" + When I POST path "/v1/admin/kafka_connector_namespaces/" with json body: + """ + { + "name": "amigos_namespace", + "cluster_id": "${connector_cluster_id}", + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + "tenant": { + "kind": "organisation", + "organisation_id": "13640221" + } + } + """ + Then the response code should be 201 + And the response should match json: + """ + { + "id": "${response.id}", + "kind": "ConnectorNamespace", + "href": "/api/connector_mgmt/v1/kafka_connector_namespaces/${response.id}", + "name": "amigos_namespace", + "owner": "Ricky Bobby", + "version": ${response.version}, + "cluster_id": "${connector_cluster_id}", + "created_at": "${response.created_at}", + "modified_at": "${response.modified_at}", + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + "tenant": { + "kind": "organisation", + "organisation_id": "13640221" + } + } + """ + And I store the ".id" selection from the response as ${namespace_id} + + # Delete namespace + Given I am logged in as "Ricky Bobby" + When I DELETE path "/v1/admin/kafka_connector_namespaces/${namespace_id}" + Then the response code should be 204 + And I GET path "/v1/admin/kafka_connector_namespaces?search=cluster_id=${connector_cluster_id}" + And the response code should be 200 + And the response should match json: + """ + { + "items": [], + "kind": "ConnectorNamespaceList", + "page": 1, + "size": 0, + "total": 0 + } + """ + + #cleanup + Given I am logged in as "El Guapo" + When I DELETE path "/v1/kafka_connector_clusters/${connector_cluster_id}" + Then the response code should be 204 + And the response should match "" diff --git a/openapi/connector_mgmt-private-admin.yaml b/openapi/connector_mgmt-private-admin.yaml index 87ec647c0..a857b7c75 100644 --- a/openapi/connector_mgmt-private-admin.yaml +++ b/openapi/connector_mgmt-private-admin.yaml @@ -15,12 +15,14 @@ servers: tags: - name: Connector Clusters Admin description: "" + - name: Connector Namespaces Admin + description: "" paths: # # These are the connector related admin APIs # - /api/connector_mgmt/v1/admin/kafka_connector_clusters/: + /api/connector_mgmt/v1/admin/kafka_connector_clusters: get: tags: - Connector Clusters Admin @@ -55,7 +57,7 @@ paths: $ref: "connector_mgmt.yaml#/components/examples/500Example" description: Unexpected error occurred security: - - Bearer: [] + - Bearer: [ ] operationId: listConnectorClusters summary: Returns a list of connector clusters @@ -107,7 +109,7 @@ paths: $ref: "connector_mgmt.yaml#/components/examples/500Example" description: Unexpected error occurred security: - - Bearer: [] + - Bearer: [ ] operationId: getConnectorUpgradesByType summary: Get a list of available connector type upgrades @@ -145,7 +147,7 @@ paths: $ref: "connector_mgmt.yaml#/components/examples/500Example" description: Unexpected error occurred security: - - Bearer: [] + - Bearer: [ ] operationId: upgradeConnectorsByType summary: upgrade a connector cluster requestBody: @@ -206,7 +208,7 @@ paths: $ref: "connector_mgmt.yaml#/components/examples/500Example" description: Unexpected error occurred security: - - Bearer: [] + - Bearer: [ ] operationId: getConnectorUpgradesByOperator summary: Get a list of available connector operator upgrades @@ -244,7 +246,7 @@ paths: $ref: "connector_mgmt.yaml#/components/examples/500Example" description: Unexpected error occurred security: - - Bearer: [] + - Bearer: [ ] operationId: upgradeConnectorsByOperator summary: upgrade a connector cluster requestBody: @@ -257,6 +259,153 @@ paths: $ref: "#/components/schemas/ConnectorAvailableOperatorUpgrade" required: true + /api/connector_mgmt/v1/admin/kafka_connector_namespaces: + get: + tags: + - Connector Namespaces Admin + parameters: + - $ref: "connector_mgmt.yaml#/components/parameters/page" + - $ref: "connector_mgmt.yaml#/components/parameters/size" + - $ref: "connector_mgmt.yaml#/components/parameters/orderBy" + - $ref: "connector_mgmt.yaml#/components/parameters/search" + responses: + "200": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/ConnectorNamespaceList" + description: Connector namespaces + "401": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/Error" + examples: + 401Example: + $ref: "connector_mgmt.yaml#/components/examples/401Example" + description: Auth token is invalid + "404": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/Error" + examples: + 404Example: + $ref: "connector_mgmt.yaml#/components/examples/404Example" + description: No matching connector namespace exists + "500": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/Error" + examples: + 500Example: + $ref: "connector_mgmt.yaml#/components/examples/500Example" + description: Unexpected error occurred + security: + - Bearer: [ ] + operationId: getConnectorNamespaces + summary: Get a list of available connector namespaces + + post: + tags: + - Connector Namespaces Admin + responses: + "202": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/ConnectorNamespace" + description: Accepted + "401": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/Error" + examples: + 401Example: + $ref: "connector_mgmt.yaml#/components/examples/401Example" + description: Auth token is invalid + "404": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/Error" + examples: + 404Example: + $ref: "connector_mgmt.yaml#/components/examples/404Example" + description: No matching connector namespace exists + "500": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/Error" + examples: + 500Example: + $ref: "connector_mgmt.yaml#/components/examples/500Example" + description: Unexpected error occurred + security: + - Bearer: [ ] + operationId: createConnectorNamespace + summary: Create a connector namespace + requestBody: + description: Namespace to create + content: + application/json: + schema: + $ref: "#/components/schemas/ConnectorNamespaceWithTenantRequest" + required: true + + /api/connector_mgmt/v1/admin/kafka_connector_namespaces/${namespace_id}: + parameters: + - name: namespace_id + description: The name of the namespace to delete + schema: + type: string + in: path + required: true + delete: + tags: + - Connector Clusters Admin + responses: + "204": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/Error" + description: Deleted + "401": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/Error" + examples: + 401Example: + $ref: "connector_mgmt.yaml#/components/examples/401Example" + description: Auth token is invalid + "404": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/Error" + examples: + 404Example: + $ref: "connector_mgmt.yaml#/components/examples/404Example" + description: No matching connector cluster exists + "500": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/Error" + examples: + 500Example: + $ref: "connector_mgmt.yaml#/components/examples/500Example" + description: Unexpected error occurred + security: + - Bearer: [ ] + operationId: deleteConnectorNamespace + summary: Delete a connector namespace + components: schemas: ConnectorAvailableTypeUpgradeList: @@ -317,6 +466,14 @@ components: available_id: type: string + ConnectorNamespaceWithTenantRequest: + allOf: + - $ref: "connector_mgmt.yaml#/components/schemas/ConnectorNamespaceRequest" + - type: object + properties: + tenant: + $ref: "connector_mgmt.yaml#/components/schemas/ConnectorNamespaceTenant" + securitySchemes: Bearer: scheme: bearer diff --git a/openapi/connector_mgmt.yaml b/openapi/connector_mgmt.yaml index 4498335a4..fea7ef3e3 100644 --- a/openapi/connector_mgmt.yaml +++ b/openapi/connector_mgmt.yaml @@ -35,7 +35,7 @@ paths: - Connector Service operationId: getVersionMetadata summary: Returns the version metadata - description: Returns the version metadata + description: Returns the version metadata responses: "200": content: @@ -63,7 +63,7 @@ paths: - Bearer: [ ] operationId: getConnectorTypeByID summary: Get a connector type by id - description: Get a connector type by id + description: Get a connector type by id responses: "200": content: @@ -361,7 +361,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ConnectorRequest" - + required: true responses: "202": @@ -697,6 +697,312 @@ paths: $ref: "#/components/examples/500Example" description: Unexpected error occurred + # + # Connector Namespaces + # + + "/api/connector_mgmt/v1/kafka_connector_namespaces": + post: + tags: + - Connector Namespaces + operationId: createConnectorNamespace + security: + - Bearer: [ ] + summary: Create a new connector namespace + description: Create a new connector namespace + requestBody: + description: Connector namespace data + content: + application/json: + schema: + $ref: "#/components/schemas/ConnectorNamespaceRequest" + examples: + ConnectorNamespaceCreateExample: + $ref: "#/components/examples/ConnectorNamespaceCreateExample" + required: true + responses: + "202": + content: + application/json: + schema: + $ref: "#/components/schemas/ConnectorNamespace" + description: Accepted + "400": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 400CreationExample: + $ref: "#/components/examples/400CreationExample" + description: Validation errors occurred + "401": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 401Example: + $ref: "#/components/examples/401Example" + description: Auth token is invalid + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 404Example: + $ref: "#/components/examples/404Example" + description: The requested resource doesn't exist + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 500Example: + $ref: "#/components/examples/500Example" + description: An unexpected error occurred creating the connector namespace + get: + tags: + - Connector Namespaces + security: + - Bearer: [ ] + operationId: listConnectorNamespaces + summary: Returns a list of connector namespaces + description: Returns a list of connector namespaces + parameters: + - $ref: "#/components/parameters/page" + - $ref: "#/components/parameters/size" + - $ref: "#/components/parameters/orderBy" + - $ref: "#/components/parameters/search" + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/ConnectorNamespaceList" + description: A list of connector namespaces + "401": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 401Example: + $ref: "#/components/examples/401Example" + description: Auth token is invalid + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 500Example: + $ref: "#/components/examples/500Example" + description: Unexpected error occurred + + "/api/connector_mgmt/v1/kafka_connector_namespaces/{connector_namespace_id}": + parameters: + - name: connector_namespace_id + description: The id of the connector namespace + schema: + type: string + in: path + required: true + get: + tags: + - Connector Namespaces + security: + - Bearer: [ ] + operationId: getConnectorNamespace + summary: Get a connector namespace + description: Get a connector namespace + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/ConnectorNamespace" + description: The connector namespace matching the request + "401": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 401Example: + $ref: "#/components/examples/401Example" + description: Auth token is invalid + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 404Example: + $ref: "#/components/examples/404Example" + description: No matching connector namespace type exists + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 500Example: + $ref: "#/components/examples/500Example" + description: Unexpected error occurred + put: + tags: + - Connector Namespaces + security: + - Bearer: [ ] + operationId: updateConnectorNamespaceById + summary: udpate a connector namespace + description: udpate a connector namespace + requestBody: + description: Data to updated connector with + content: + application/json: + schema: + $ref: "#/components/schemas/ConnectorNamespaceRequest" + required: true + responses: + "204": + description: Namespace status is updated + "401": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 401Example: + $ref: "#/components/examples/401Example" + description: Auth token is invalid + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 404Example: + $ref: "#/components/examples/404Example" + description: No matching connector namespace exists + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 500Example: + $ref: "#/components/examples/500Example" + description: Unexpected error occurred + delete: + tags: + - Connector Namespaces + security: + - Bearer: [ ] + operationId: deleteConnectorNamespace + summary: Delete a connector namespace + description: Delete a connector namespace + responses: + "204": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + description: Deleted + "401": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 401Example: + $ref: "#/components/examples/401Example" + description: Auth token is invalid + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 404DeleteExample: + $ref: "#/components/examples/404DeleteExample" + description: No resource with specified ID exists + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 500DeleteExample: + $ref: "#/components/examples/500DeleteExample" + description: Unexpected error occurred + + "/api/connector_mgmt/v1/kafka_connector_namespaces/eval": + post: + tags: + - Connector Namespaces + operationId: createEvaluationNamespace + security: + - Bearer: [ ] + summary: Create a new short lived evaluation connector namespace + description: Create a new evaluation connector namespace + requestBody: + description: Connector namespace data + content: + application/json: + schema: + $ref: "#/components/schemas/ConnectorNamespaceEvalRequest" + examples: + ConnectorNamespaceEvalCreateExample: + $ref: "#/components/examples/ConnectorNamespaceEvalCreateExample" + required: true + responses: + "202": + content: + application/json: + schema: + $ref: "#/components/schemas/ConnectorNamespace" + description: Accepted + "400": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 400CreationExample: + $ref: "#/components/examples/400CreationExample" + description: Validation errors occurred + "401": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 401Example: + $ref: "#/components/examples/401Example" + description: Auth token is invalid + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 404Example: + $ref: "#/components/examples/404Example" + description: The requested resource doesn't exist + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 500Example: + $ref: "#/components/examples/500Example" + description: An unexpected error occurred creating the connector namespace + components: schemas: @@ -781,8 +1087,10 @@ components: propertyName: kind mapping: ConnectorCluster: "#/components/schemas/ConnectorClusterTarget" + ConnectorNamespace: "#/components/schemas/ConnectorNamespaceTarget" oneOf: - $ref: "#/components/schemas/ConnectorClusterTarget" + - $ref: "#/components/schemas/ConnectorNamespaceTarget" ConnectorClusterTarget: description: "Targets workloads to an addon cluster" @@ -795,6 +1103,17 @@ components: cluster_id: type: string + ConnectorNamespaceTarget: + description: "Targets workloads to an addon cluster namespace" + type: object + required: + - kind + properties: + kind: + type: string + namespace_id: + type: string + VersionMetadata: allOf: - $ref: "#/components/schemas/ObjectReference" @@ -1080,6 +1399,115 @@ components: items: $ref: "#/components/schemas/ConnectorType" + # + # Connector Namespaces + # + ConnectorNamespaceRequestMeta: + required: + - name + type: object + properties: + name: + type: string + annotations: + type: array + items: + type: object + properties: + name: + type: string + value: + type: string + required: + - name + - value + + ConnectorNamespaceMeta: + allOf: + - $ref: "#/components/schemas/ObjectMeta" + - $ref: "#/components/schemas/ConnectorNamespaceRequestMeta" + + ConnectorNamespaceTenant: + type: object + required: + - kind + discriminator: + propertyName: kind + mapping: + user: "#/components/schemas/ConnectorNamespaceTenantUser" + organisation: "#/components/schemas/ConnectorNamespaceTenantOrganisation" + oneOf: + - $ref: "#/components/schemas/ConnectorNamespaceTenantUser" + - $ref: "#/components/schemas/ConnectorNamespaceTenantOrganisation" + + ConnectorNamespaceTenantUser: + type: object + properties: + kind: + type: string + user_id: + type: string + + ConnectorNamespaceTenantOrganisation: + type: object + properties: + kind: + type: string + organisation_id: + type: string + + ConnectorNamespaceRequest: + description: A connector namespace create request + allOf: + - $ref: "#/components/schemas/ConnectorNamespaceEvalRequest" + - type: object + properties: + cluster_id: + type: string + required: + - name + + ConnectorNamespaceEvalRequest: + description: An evaluation connector namespace create request + allOf: + - $ref: "#/components/schemas/ConnectorNamespaceRequestMeta" + + ConnectorNamespaceList: + allOf: + - $ref: "#/components/schemas/List" + - type: object + properties: + items: + type: array + items: + $ref: "#/components/schemas/ConnectorNamespace" + + ConnectorNamespace: + description: A connector namespace + allOf: + - $ref: "#/components/schemas/ObjectReference" + - $ref: "#/components/schemas/ConnectorNamespaceMeta" + - type: object + properties: + name: + type: string + version: + type: integer + format: int64 + cluster_id: + type: string + expiration: + #format: date-time + type: string + tenant: + $ref: "#/components/schemas/ConnectorNamespaceTenant" + required: + - id + - name + - version + - cluster_id + - tenant + parameters: id: name: id @@ -1204,7 +1632,7 @@ components: schema: properties: common: - required: [] + required: [ ] title: Log type: object properties: @@ -1317,6 +1745,19 @@ components: error_handling: dead_letter_queue: topic: "dlq" + ConnectorNamespaceCreateExample: + value: + name: "MyNamespace" + cluster_id: "9bsv0s7tne7g02gh5g4g" + annotations: + - name: "connector_mgmt.api.openshift.com/profile" + value: "default-profile" + ConnectorNamespaceEvalCreateExample: + value: + name: "MyEvalNamespace" + annotations: + - name: "connector_mgmt.api.openshift.com/profile" + value: "default-profile" 400CreationExample: value: id: "103" diff --git a/test/cucumber/cucumber.go b/test/cucumber/cucumber.go index 3171d0534..c3b15cc38 100644 --- a/test/cucumber/cucumber.go +++ b/test/cucumber/cucumber.go @@ -59,10 +59,11 @@ type TestSuite struct { // TestUser represents a user that can login to the system. The same users are shared by // the different test scenarios. type TestUser struct { - Name string - Token string - Ctx context.Context - Mu sync.Mutex + Name string + Token string + UserName string + Ctx context.Context + Mu sync.Mutex } // TestScenario holds that state of single scenario. It is not accessed @@ -168,10 +169,10 @@ func (s *TestScenario) Expand(value string, skippedVars []string) (result string switch next := next.(type) { case string: return next - case float64: - return fmt.Sprintf("%f", next) case float32: - return fmt.Sprintf("%f", next) + case float64: + // handle int64 returned as float in json + return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%f", next), "0"), ".") case nil: rerr = fmt.Errorf("field ${%s} not found in json response:\n%s\n", name, string(session.RespBytes)) return "" diff --git a/test/cucumber/user.go b/test/cucumber/user.go index 94fc1a363..78d2174d4 100644 --- a/test/cucumber/user.go +++ b/test/cucumber/user.go @@ -27,6 +27,7 @@ func init() { ctx.Step(`^I am logged in as "([^"]*)"$`, s.iAmLoggedInAs) ctx.Step(`^I set the "([^"]*)" header to "([^"]*)"$`, s.iSetTheHeaderTo) ctx.Step(`^an admin user named "([^"]+)" with roles "([^"]+)"$`, s.Suite.createAdminUserNamed) + ctx.Step(`^I store userid for "([^"]+)" as \${([^"]*)}$`, s.storeUserId) }) } @@ -58,6 +59,7 @@ func (s *TestSuite) createUserNamedInOrganization(name string, orgid string) err s.users[name] = &TestUser{ Name: name, Token: token, + UserName: account.Username(), Ctx: context.WithValue(context.Background(), compat.ContextAccessToken, token), } return nil @@ -92,11 +94,24 @@ func (s *TestSuite) createAdminUserNamed(name, roles string) error { s.users[name] = &TestUser{ Name: name, Token: token, + UserName: account.Username(), Ctx: context.WithValue(context.Background(), compat.ContextAccessToken, token), } return nil } +func (s *TestScenario) storeUserId(name, varName string) error { + s.Suite.Mu.Lock() + defer s.Suite.Mu.Unlock() + + user := s.Suite.users[name] + if user != nil { + s.Variables[varName] = user.UserName + } + + return nil +} + func (s *TestScenario) iAmLoggedInAs(name string) error { s.Session().Header.Del("Authorization") s.CurrentUser = name From 48a98d6c675ec5838cb714f74f121f824fab97f3 Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Wed, 23 Feb 2022 16:20:36 -0800 Subject: [PATCH 02/12] feat(connectors): replaced cluster with namespace in connector deployments, filtering by namespaces in connector queries --- .../api/admin/private/api/openapi.yaml | 6 + ...el_connector_available_operator_upgrade.go | 1 + .../model_connector_available_type_upgrade.go | 1 + .../connector/internal/api/dbapi/connector.go | 18 +- .../{namespace.go => connector_namespace.go} | 0 .../internal/api/private/api/openapi.yaml | 4 + .../model_connector_deployment_spec.go | 2 + .../internal/api/public/api/openapi.yaml | 27 +- .../public/model_connector_cluster_target.go | 16 - .../model_connector_namespace_target.go | 16 - .../api/public/model_deployment_location.go | 2 - .../connector/internal/config/connectors.go | 4 +- .../internal/environments/development.go | 1 + .../internal/environments/integration.go | 1 + .../internal/environments/production.go | 17 +- .../connector/internal/environments/stage.go | 13 +- .../connector/internal/generated/bindata.go | 4 +- .../internal/handlers/connector_admin.go | 4 +- .../internal/handlers/connector_agent.go | 1 + .../internal/handlers/connector_cluster.go | 15 +- .../internal/handlers/connector_validation.go | 3 - .../connector/internal/handlers/connectors.go | 12 +- .../internal/handlers/namespace_validation.go | 20 ++ ...0000_add_connector_namespace_deployment.go | 56 ++++ .../internal/migrations/migrations.go | 1 + .../internal/presenters/connector.go | 16 +- .../connector_available_operator_upgrade.go | 2 + .../connector_available_type_upgrade.go | 2 + .../presenters/connector_deployment.go | 2 + .../presenters/connector_namespace.go | 47 +-- .../internal/presenters/connector_request.go | 8 +- .../internal/services/connector_cluster.go | 185 +++++++++--- ...ace_service.go => connector_namespaces.go} | 81 ++++- .../connector/internal/services/connectors.go | 18 +- .../internal/workers/connector_mgr.go | 15 +- internal/connector/providers.go | 3 +- .../features/connector-agent-api.feature | 29 +- .../features/connector-api.feature | 30 +- .../connector-multitenancy-api.feature | 281 ++++++++++++++---- .../features/connector-old-path.feature | 2 - openapi/connector_mgmt-private-admin.yaml | 4 + openapi/connector_mgmt-private.yaml | 4 + openapi/connector_mgmt.yaml | 29 +- test/cucumber/http_response.go | 2 +- 44 files changed, 704 insertions(+), 301 deletions(-) rename internal/connector/internal/api/dbapi/{namespace.go => connector_namespace.go} (100%) delete mode 100644 internal/connector/internal/api/public/model_connector_cluster_target.go delete mode 100644 internal/connector/internal/api/public/model_connector_namespace_target.go create mode 100644 internal/connector/internal/handlers/namespace_validation.go create mode 100644 internal/connector/internal/migrations/202202220000_add_connector_namespace_deployment.go rename internal/connector/internal/services/{namespace_service.go => connector_namespaces.go} (76%) diff --git a/internal/connector/internal/api/admin/private/api/openapi.yaml b/internal/connector/internal/api/admin/private/api/openapi.yaml index c7903df58..c7fe5df6e 100644 --- a/internal/connector/internal/api/admin/private/api/openapi.yaml +++ b/internal/connector/internal/api/admin/private/api/openapi.yaml @@ -678,11 +678,14 @@ components: available_id: 6 assigned_id: 0 connector_id: connector_id + namespace: namespace channel: channel connector_type_id: connector_type_id properties: connector_id: type: string + namespace: + type: string connector_type_id: type: string channel: @@ -698,6 +701,7 @@ components: description: An available operator upgrade for a connector example: connector_id: connector_id + namespace: namespace channel: channel operator: available_id: available_id @@ -706,6 +710,8 @@ components: properties: connector_id: type: string + namespace: + type: string connector_type_id: type: string channel: diff --git a/internal/connector/internal/api/admin/private/model_connector_available_operator_upgrade.go b/internal/connector/internal/api/admin/private/model_connector_available_operator_upgrade.go index 8e9fefe01..a8bc5f20a 100644 --- a/internal/connector/internal/api/admin/private/model_connector_available_operator_upgrade.go +++ b/internal/connector/internal/api/admin/private/model_connector_available_operator_upgrade.go @@ -12,6 +12,7 @@ package private // ConnectorAvailableOperatorUpgrade An available operator upgrade for a connector type ConnectorAvailableOperatorUpgrade struct { ConnectorId string `json:"connector_id,omitempty"` + Namespace string `json:"namespace,omitempty"` ConnectorTypeId string `json:"connector_type_id,omitempty"` Channel string `json:"channel,omitempty"` Operator ConnectorAvailableOperatorUpgradeOperator `json:"operator,omitempty"` diff --git a/internal/connector/internal/api/admin/private/model_connector_available_type_upgrade.go b/internal/connector/internal/api/admin/private/model_connector_available_type_upgrade.go index bf7bda5d1..eb13a8507 100644 --- a/internal/connector/internal/api/admin/private/model_connector_available_type_upgrade.go +++ b/internal/connector/internal/api/admin/private/model_connector_available_type_upgrade.go @@ -12,6 +12,7 @@ package private // ConnectorAvailableTypeUpgrade An available type upgrade for a connector type ConnectorAvailableTypeUpgrade struct { ConnectorId string `json:"connector_id,omitempty"` + Namespace string `json:"namespace,omitempty"` ConnectorTypeId string `json:"connector_type_id,omitempty"` Channel string `json:"channel,omitempty"` ShardMetadata ConnectorAvailableTypeUpgradeShardMetadata `json:"shard_metadata,omitempty"` diff --git a/internal/connector/internal/api/dbapi/connector.go b/internal/connector/internal/api/dbapi/connector.go index df36896e6..ff723997a 100644 --- a/internal/connector/internal/api/dbapi/connector.go +++ b/internal/connector/internal/api/dbapi/connector.go @@ -47,11 +47,11 @@ var AllTargetKind = []TargetKind{ type Connector struct { api.Meta - TargetKind TargetKind - AddonClusterId string - CloudProvider string - Region string - MultiAZ bool + TargetKind TargetKind + NamespaceId *string + CloudProvider string + Region string + MultiAZ bool Name string Owner string @@ -71,8 +71,8 @@ type Connector struct { type ConnectorStatus struct { api.Meta - ClusterID string - Phase string + NamespaceID *string + Phase string } type ConnectorList []*Connector @@ -86,6 +86,8 @@ type ConnectorDeployment struct { ConnectorVersion int64 ConnectorTypeChannelId int64 ClusterID string + NamespaceID string + NamespaceName string `gorm:"->"` // readonly field, used to join connector_namespaces AllowUpgrade bool Status ConnectorDeploymentStatus `gorm:"foreignKey:ID"` } @@ -121,6 +123,7 @@ type ConnectorDeploymentTypeUpgrade struct { ConnectorID string `json:"connector_id,omitempty"` DeploymentID string `json:"deployment_id,omitempty"` ConnectorTypeId string `json:"connector_type_id,omitempty"` + Namespace string `json:"namespace,omitempty"` Channel string `json:"channel,omitempty"` ShardMetadata *ConnectorTypeUpgrade `json:"shard_metadata,omitempty"` } @@ -136,6 +139,7 @@ type ConnectorDeploymentOperatorUpgrade struct { ConnectorID string `json:"connector_id,omitempty"` DeploymentID string `json:"deployment_id,omitempty"` ConnectorTypeId string `json:"connector_type_id,omitempty"` + Namespace string `json:"namespace,omitempty"` Channel string `json:"channel,omitempty"` Operator *ConnectorOperatorUpgrade `json:"operator,omitempty"` } diff --git a/internal/connector/internal/api/dbapi/namespace.go b/internal/connector/internal/api/dbapi/connector_namespace.go similarity index 100% rename from internal/connector/internal/api/dbapi/namespace.go rename to internal/connector/internal/api/dbapi/connector_namespace.go diff --git a/internal/connector/internal/api/private/api/openapi.yaml b/internal/connector/internal/api/private/api/openapi.yaml index ec3acfcdc..0db1eb095 100644 --- a/internal/connector/internal/api/private/api/openapi.yaml +++ b/internal/connector/internal/api/private/api/openapi.yaml @@ -351,6 +351,10 @@ components: type: integer connector_type_id: type: string + namespace_id: + type: string + namespace_name: + type: string connector_spec: type: object allow_upgrade: diff --git a/internal/connector/internal/api/private/model_connector_deployment_spec.go b/internal/connector/internal/api/private/model_connector_deployment_spec.go index 232586a62..812b85d6e 100644 --- a/internal/connector/internal/api/private/model_connector_deployment_spec.go +++ b/internal/connector/internal/api/private/model_connector_deployment_spec.go @@ -17,6 +17,8 @@ type ConnectorDeploymentSpec struct { ConnectorId string `json:"connector_id,omitempty"` ConnectorResourceVersion int64 `json:"connector_resource_version,omitempty"` ConnectorTypeId string `json:"connector_type_id,omitempty"` + NamespaceId string `json:"namespace_id,omitempty"` + NamespaceName string `json:"namespace_name,omitempty"` ConnectorSpec map[string]interface{} `json:"connector_spec,omitempty"` // allow the connector to upgrade to a new operator // Deprecated diff --git a/internal/connector/internal/api/public/api/openapi.yaml b/internal/connector/internal/api/public/api/openapi.yaml index e5cc0dde6..a245e5189 100644 --- a/internal/connector/internal/api/public/api/openapi.yaml +++ b/internal/connector/internal/api/public/api/openapi.yaml @@ -1346,8 +1346,7 @@ components: value: name: MyLogger deployment_location: - kind: ConnectoCluster - cluster_id: 9bsv0s7tne7g02gh5g4g + namespace_id: 9bsv0s7tne7g02gh5g4g kafka: id: 9bsv0s6brfr002pfnkh0 client_id: srvc-acct-162ef2d8-0209-4117-8462-df63c2025c26 @@ -1622,33 +1621,9 @@ components: - client_id - client_secret DeploymentLocation: - discriminator: - mapping: - ConnectorCluster: '#/components/schemas/ConnectorClusterTarget' - ConnectorNamespace: '#/components/schemas/ConnectorNamespaceTarget' - propertyName: kind - oneOf: - - $ref: '#/components/schemas/ConnectorClusterTarget' - - $ref: '#/components/schemas/ConnectorNamespaceTarget' - ConnectorClusterTarget: - description: Targets workloads to an addon cluster - properties: - kind: - type: string - cluster_id: - type: string - required: - - kind - type: object - ConnectorNamespaceTarget: - description: Targets workloads to an addon cluster namespace properties: - kind: - type: string namespace_id: type: string - required: - - kind type: object VersionMetadata: allOf: diff --git a/internal/connector/internal/api/public/model_connector_cluster_target.go b/internal/connector/internal/api/public/model_connector_cluster_target.go deleted file mode 100644 index 7bb8b5b55..000000000 --- a/internal/connector/internal/api/public/model_connector_cluster_target.go +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Connector Service Fleet Manager - * - * Connector Service Fleet Manager is a Rest API to manage connectors. - * - * API version: 0.1.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package public - -// ConnectorClusterTarget Targets workloads to an addon cluster -type ConnectorClusterTarget struct { - Kind string `json:"kind"` - ClusterId string `json:"cluster_id,omitempty"` -} diff --git a/internal/connector/internal/api/public/model_connector_namespace_target.go b/internal/connector/internal/api/public/model_connector_namespace_target.go deleted file mode 100644 index 40c09d0c7..000000000 --- a/internal/connector/internal/api/public/model_connector_namespace_target.go +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Connector Service Fleet Manager - * - * Connector Service Fleet Manager is a Rest API to manage connectors. - * - * API version: 0.1.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package public - -// ConnectorNamespaceTarget Targets workloads to an addon cluster namespace -type ConnectorNamespaceTarget struct { - Kind string `json:"kind"` - NamespaceId string `json:"namespace_id,omitempty"` -} diff --git a/internal/connector/internal/api/public/model_deployment_location.go b/internal/connector/internal/api/public/model_deployment_location.go index bedb7a43d..b25164f3c 100644 --- a/internal/connector/internal/api/public/model_deployment_location.go +++ b/internal/connector/internal/api/public/model_deployment_location.go @@ -11,7 +11,5 @@ package public // DeploymentLocation struct for DeploymentLocation type DeploymentLocation struct { - Kind string `json:"kind"` - ClusterId string `json:"cluster_id,omitempty"` NamespaceId string `json:"namespace_id,omitempty"` } diff --git a/internal/connector/internal/config/connectors.go b/internal/connector/internal/config/connectors.go index 220e50703..be0ef6449 100644 --- a/internal/connector/internal/config/connectors.go +++ b/internal/connector/internal/config/connectors.go @@ -44,8 +44,8 @@ func NewConnectorsConfig() *ConnectorsConfig { func (c *ConnectorsConfig) AddFlags(fs *pflag.FlagSet) { fs.StringArrayVar(&c.ConnectorCatalogDirs, "connector-catalog", c.ConnectorCatalogDirs, "Directory containing connector catalog entries") - fs.StringArrayVar(&c.ConnectorEvalOrganizations, "connector-eval-duration", c.ConnectorCatalogDirs, "Connector eval duration in go duration format") - fs.StringArrayVar(&c.ConnectorEvalOrganizations, "connector-eval-organizations", c.ConnectorCatalogDirs, "Connector eval organization IDs") + fs.StringVar(&c.ConnectorEvalDurationString, "connector-eval-duration", c.ConnectorEvalDurationString, "Connector eval duration in golang duration format") + fs.StringArrayVar(&c.ConnectorEvalOrganizations, "connector-eval-organizations", c.ConnectorEvalOrganizations, "Connector eval organization IDs") } func (c *ConnectorsConfig) ReadFiles() error { diff --git a/internal/connector/internal/environments/development.go b/internal/connector/internal/environments/development.go index 4daef64ec..9088ad205 100644 --- a/internal/connector/internal/environments/development.go +++ b/internal/connector/internal/environments/development.go @@ -19,5 +19,6 @@ func NewDevelopmentEnvLoader() environments.EnvLoader { "mas-sso-base-url": "https://identity.api.stage.openshift.com", "mas-sso-realm": "rhoas", "osd-idp-mas-sso-realm": "rhoas-kafka-sre", + "connector-eval-duration": "48h", } } diff --git a/internal/connector/internal/environments/integration.go b/internal/connector/internal/environments/integration.go index e5c232811..c72073708 100644 --- a/internal/connector/internal/environments/integration.go +++ b/internal/connector/internal/environments/integration.go @@ -33,6 +33,7 @@ func (b IntegrationEnvLoader) Defaults() map[string]string { "max-allowed-instances": "1", "mas-sso-base-url": "https://identity.api.stage.openshift.com", "mas-sso-realm": "rhoas", + "connector-eval-duration": "48h", } } diff --git a/internal/connector/internal/environments/production.go b/internal/connector/internal/environments/production.go index 669a49675..69bc5d27c 100644 --- a/internal/connector/internal/environments/production.go +++ b/internal/connector/internal/environments/production.go @@ -4,13 +4,14 @@ import "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/environments" func NewProductionEnvLoader() environments.EnvLoader { return environments.SimpleEnvLoader{ - "v": "1", - "ocm-debug": "false", - "enable-ocm-mock": "false", - "enable-sentry": "true", - "enable-deny-list": "true", - "max-allowed-instances": "1", - "mas-sso-realm": "rhoas", - "mas-sso-base-url": "https://identity.api.openshift.com", + "v": "1", + "ocm-debug": "false", + "enable-ocm-mock": "false", + "enable-sentry": "true", + "enable-deny-list": "true", + "max-allowed-instances": "1", + "mas-sso-realm": "rhoas", + "mas-sso-base-url": "https://identity.api.openshift.com", + "connector-eval-duration": "48h", } } diff --git a/internal/connector/internal/environments/stage.go b/internal/connector/internal/environments/stage.go index cc81ab4ab..69319d4b7 100644 --- a/internal/connector/internal/environments/stage.go +++ b/internal/connector/internal/environments/stage.go @@ -4,11 +4,12 @@ import "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/environments" func NewStageEnvLoader() environments.EnvLoader { return environments.SimpleEnvLoader{ - "ocm-base-url": "https://api.stage.openshift.com", - "enable-ocm-mock": "false", - "enable-deny-list": "true", - "max-allowed-instances": "1", - "mas-sso-base-url": "https://identity.api.stage.openshift.com", - "mas-sso-realm": "rhoas", + "ocm-base-url": "https://api.stage.openshift.com", + "enable-ocm-mock": "false", + "enable-deny-list": "true", + "max-allowed-instances": "1", + "mas-sso-base-url": "https://identity.api.stage.openshift.com", + "mas-sso-realm": "rhoas", + "connector-eval-duration": "48h", } } diff --git a/internal/connector/internal/generated/bindata.go b/internal/connector/internal/generated/bindata.go index 3673d0340..32c51fb73 100644 --- a/internal/connector/internal/generated/bindata.go +++ b/internal/connector/internal/generated/bindata.go @@ -78,7 +78,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _connector_mgmtYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x6d\x73\xdb\x38\x92\xf0\x77\xff\x0a\x3c\xca\xb3\xe5\xdd\x3b\x4b\x96\x64\xf9\x4d\x75\xd9\x2a\xc7\x76\x32\x9e\x71\x9c\x8c\xed\x4c\x26\xbb\xb5\x25\x43\x24\x24\x21\x26\x01\x1a\x80\x64\x2b\xbb\xf7\xdf\xaf\x00\x90\x22\x40\x82\x14\x25\x2b\xb6\x33\x23\x56\xed\x4e\x4c\x02\x8d\xee\x46\xa3\xd1\x6f\x80\x68\x84\x08\x8c\x70\x17\xec\x34\x9a\x8d\x26\x78\x05\x08\x42\x3e\x10\x23\xcc\x01\xe4\x60\x80\x19\x17\x20\xc0\x04\x01\x41\x01\x0c\x02\x7a\x0f\x38\x0d\x11\x38\x3b\x39\xe5\xf2\xd5\x2d\xa1\xf7\xba\xb5\xec\x40\x40\x0c\x0e\xf8\xd4\x1b\x87\x88\x88\xc6\xc6\x2b\x70\x14\x04\x00\x11\x3f\xa2\x98\x08\x0e\x7c\x34\xc0\x04\xf9\x60\x84\x18\x02\xf7\x38\x08\x40\x1f\x01\x1f\x73\x8f\x4e\x10\x83\xfd\x00\x81\xfe\x54\x8e\x04\xc6\x1c\x31\xde\x00\x67\x03\x20\x54\x5b\x39\x40\x8c\x1d\x05\xb7\x08\x45\x1a\x93\x19\xe4\x8d\x57\xa0\x16\x31\x3c\x81\x02\xd5\xb6\x00\xf4\x25\x15\x28\x94\x8d\xc5\x08\x81\x9a\x47\x09\x41\x9e\xa0\xac\x17\x0e\x43\x51\x8f\x5b\x36\xa6\x30\x0c\x6a\x60\x80\x03\xb4\x81\xc9\x80\x76\x37\x00\x10\x58\x04\xa8\x0b\x8e\x93\x0e\xe0\x0a\xb1\x09\xf6\x10\x78\x1b\x20\x24\xc0\x7b\x48\xe0\x10\xb1\x0d\x00\x26\x88\x71\x4c\x49\x17\x34\x1b\xad\x46\x73\x03\x00\x1f\x71\x8f\xe1\x48\xa8\x97\x73\xfa\x6b\x7a\x2e\x11\x17\xe0\xe8\xe3\x99\x44\x33\x54\x1f\xc0\x0c\x51\xde\xd8\xe0\x88\xc9\x41\x24\x56\x75\x30\x66\x41\x17\x8c\x84\x88\x78\x77\x7b\x1b\x46\xb8\x21\x99\xcd\x47\x78\x20\x1a\x1e\x0d\x37\x00\xc8\x20\xf0\x1e\x62\x02\xfe\x1a\x31\xea\x8f\x3d\xf9\xe6\x6f\x40\x83\x73\x03\xe3\x02\x0e\xd1\x3c\x90\x57\x02\x0e\x31\x19\x3a\x01\x75\xb7\xb7\x03\xea\xc1\x60\x44\xb9\xe8\x1e\x34\x9b\xcd\x7c\xf7\xd9\xf7\xb4\xe7\x76\xbe\x95\x37\x66\x0c\x11\x01\x7c\x1a\x42\x4c\x36\x04\x1c\xc6\x0c\x20\x30\xb4\xe6\xe5\x7a\x1a\x21\x9e\xef\x5f\xab\xb9\x5a\x57\x6e\x08\x8e\x83\x31\x17\x68\x81\x0e\xf1\xfc\x3a\xdb\x6f\x44\x50\x8c\x14\xfe\xaf\xe4\xff\x80\xb3\xdb\xab\x8d\x0d\x00\x6a\x72\x1a\xb6\x6d\x31\xdd\x9e\xb4\x6a\x5d\x05\x77\x88\x84\xfe\x07\x00\x09\x43\xf4\x53\x2f\x40\x04\xc8\xb5\xc8\xa0\x44\xe4\xcc\xef\xca\xfe\xbf\x69\x71\x7d\x8f\x04\xf4\xa1\x80\x71\x2b\x3e\x0e\x43\xc8\xa6\x5d\x70\x89\xc4\x98\x11\xae\x56\x4b\x2c\xd9\x20\xb4\xdb\x5a\xc4\x55\x68\xcf\x10\x8f\x28\xe1\xc8\x40\xb7\xd6\x6e\x36\x6b\xe9\x9f\x40\x8a\xbb\x40\x44\x98\xaf\x00\x80\x51\x14\x60\x4f\x21\xbf\xfd\x95\x53\x62\x7f\x05\x80\x7b\x23\x14\xc2\xec\x5b\x00\xfe\x3f\x43\x83\x2e\xd8\x7c\xb5\xed\xd1\x30\xa2\x04\x11\xc1\xb7\x75\x5b\xbe\x9d\x21\x7f\xd3\xe8\x6c\xd1\xf5\x5b\x96\x96\xd9\xdc\xe5\x25\xaf\x6c\xe2\xb6\x6f\xe1\xe0\x16\xf6\xd2\xf7\x42\x76\xda\xfe\xb7\xfd\xa2\x87\xfd\xff\x8d\xf9\x11\x41\x06\x43\x24\xe2\xf5\xae\xe7\x56\x8b\x5a\xae\xcb\x86\x13\xf3\xeb\x11\x02\xd8\x07\x54\x69\xcc\xb4\x13\x90\x9d\x36\x8a\x59\x27\x3f\x77\x01\x17\x0c\x93\xe1\xec\x35\x26\x5d\x20\x45\x77\xf6\x82\xa1\xbb\x31\x66\xc8\xef\x02\xc1\xc6\xa8\xba\x4c\xa6\x8b\x14\x00\x8e\xbc\x31\xc3\x62\x6a\xb6\x7c\x83\x20\x43\xac\x0b\xfe\x09\xfe\x55\x20\xb7\x33\x58\x12\xd4\x9b\xe9\xd9\x49\x56\x72\xdf\x21\x01\x60\x86\x5e\xb9\x8b\xcc\xf8\x64\x71\x69\x6e\xeb\x67\x92\xda\x9a\x53\x6a\x2d\xe2\x6b\x99\xae\xe8\x01\x86\x51\x60\x22\x9a\x3c\x56\xb7\x53\xdd\x2c\xdf\xca\x3d\x74\x02\x75\xdb\x05\xa4\x56\xb4\x6c\xae\x73\x22\x07\x42\x28\xbc\x91\xdc\x2e\xa4\x38\x4a\xf9\x41\x4a\xf3\xc7\x2c\xed\x34\x5b\xcf\xc3\xd2\x53\xc6\x28\xab\xce\xca\x4e\xb3\xb5\x2c\x03\xd3\xae\x85\x6c\x3b\x1a\x8b\x11\x10\xf4\x16\x11\x69\x10\x60\x32\x81\x81\xb1\xbc\x6b\x9d\x66\xe7\x07\x61\x52\x67\x79\x26\x75\xe6\x31\xe9\x82\xa6\xb2\x94\x91\x31\xf4\x80\xb9\xe0\x29\xc3\x76\x9f\x6b\xa1\x2e\xc8\xb0\xdd\x66\x73\x59\x86\xa5\x5d\x0b\x19\xf6\x89\xa0\x87\x08\x79\x02\xf9\x00\x49\xbc\x00\xf5\x94\x55\xe5\x2f\xbc\x5f\x2d\x62\x7e\xac\x58\xd5\xf3\x22\x0b\x05\x82\x00\x73\x21\xf7\x39\x5b\x18\x78\x99\x99\x32\xaf\x53\x7e\xf7\x95\x28\xbb\x26\x22\x6d\xb9\x1d\xc1\xa1\x31\x09\x73\x9b\x73\xfc\x6d\x91\xe6\x94\xf9\x88\xbd\x99\x2e\x32\x00\x82\xcc\x1b\xd5\x5e\xfc\x46\x76\x8e\xb9\x28\x56\x89\x73\x66\x6a\xbd\x77\x54\xdb\x3b\xd6\xaa\x70\xae\x2a\xcc\xd8\xf5\x0b\x5a\xf4\x89\x72\x8c\xa4\xc7\x3b\x4f\x3b\x3e\x42\x31\x7a\x0c\x41\x81\x4c\x2c\x81\xa9\x16\x8f\xd5\x67\x15\x1c\xb9\x4f\x97\x8c\x4b\x17\x96\xb6\x74\x2b\x40\xe9\x07\xdc\x8d\x11\x9b\x1a\xfc\xd5\x4e\x09\xe4\x53\xe2\x15\x71\xfd\x23\x62\x03\xca\x42\x65\xf9\x41\x15\x7d\x00\x98\x00\x48\x74\xaf\x11\xa3\x84\x8e\x39\x08\x21\x21\x88\x6d\x94\x4b\x9b\x76\x4f\xfa\x94\x06\x08\x12\xe3\x8b\xc3\x21\x01\x89\x95\xf9\x86\xfa\x06\x83\x0b\xc2\x32\x86\xa3\xea\x5c\x1c\xe5\x4b\xc3\xbd\x30\x2a\x69\xc0\x4b\x8d\xa4\xbd\x42\x8a\xd6\xc7\xac\x97\x9e\xbc\xc2\x95\x52\xcd\x92\xb7\x80\xd4\xca\x9c\xbb\xa2\xed\xa3\xfd\xcc\xdb\x47\xb1\x36\xf4\x3c\x14\x09\x64\x19\xcf\x3f\x86\x02\xec\x34\x9b\x6a\x5e\x30\x25\xcb\xef\x16\x59\x10\x85\x7c\xfa\x4d\xee\x12\xaa\xa5\x56\x88\x3c\xd5\x88\x06\xe7\xd6\xfb\xeb\xda\x37\xab\xe4\x9b\x5d\xa7\xbe\x3d\xf2\xa5\xce\xa0\x63\xe6\x21\xe0\x53\xc4\xc9\xa6\xd0\xfe\xd9\xda\x26\xc9\x08\x16\x01\xe3\x22\xb3\x44\xef\xf6\x49\xd4\xc4\xde\xa4\xab\x78\x61\x8f\xb0\x33\xa4\xd9\x9d\x87\xf3\x07\xf3\xbe\x5e\xb4\x6f\xb4\xa8\x5f\xb4\x76\x89\xd6\x2e\xd1\xf3\x44\x87\xf8\xf6\xbf\xcb\x33\x17\x73\x16\x23\xf6\x6b\x4f\xa1\xd2\xcc\x98\x52\x56\xa1\x65\x12\x01\x2e\xf5\xe5\x6e\xf2\x32\x75\x47\xc5\xc8\xfc\x3a\x28\xbf\x36\xfc\xaa\x30\x69\x1d\x94\x5f\x88\x61\x8f\x52\xbb\xba\x69\x80\x04\xfa\x9e\xba\x50\x8f\x50\xa8\x0e\x4f\xd4\xe7\x79\x1a\xb1\xb0\x95\x5b\x29\xbe\x94\x85\xe2\xa0\x61\xed\xee\xfe\x61\xb5\x9e\x9e\xe0\x47\xe8\x3e\x0b\x40\x99\x06\x54\x56\x51\xb2\x8d\x82\x7b\x2c\x46\x80\x47\xc8\xc3\x03\x8c\x7c\x70\x76\xf2\x23\x6b\xc2\xc7\x31\x31\x0b\x60\x49\xad\x18\xc9\x1d\xe6\x7b\x2a\x45\x35\x40\xa1\x4e\xfc\x28\xbf\xce\x53\x89\x45\x8d\xe6\xc7\xa2\x4f\xa0\x80\x40\x50\x8d\x44\xa6\x68\x47\xca\xd2\x46\x89\x98\x98\x42\x12\x22\x36\x44\x75\x05\xe5\xbf\xab\x46\xaa\x75\x58\x9d\xf6\xbf\x22\x4f\x14\x80\x95\xa0\x16\x84\x9a\x71\x58\x7f\xbe\xfa\x70\xa1\xf9\xb3\x05\x2e\xdf\x1e\x83\xbd\xc3\x66\x1b\xd4\x67\x75\x87\x82\xd2\x80\x37\x30\x12\x83\x06\x65\xc3\xed\x91\x08\x83\x6d\x36\xf0\x64\xab\xe5\xb0\x5d\x7d\x88\x7e\xd6\xf9\x8f\x10\x22\x5f\x7b\x02\x7f\xde\x3d\x71\xed\x09\xfc\x08\x9e\x80\xa3\xd6\x34\x2e\x47\x5e\xb4\xda\xd4\x8b\xab\x98\x17\xc9\x51\xdb\xa5\xcf\xe5\x59\xe8\x14\x2d\x50\x79\xe7\x9d\x93\xb2\x06\x9e\x05\xb3\x42\xea\x3a\xd3\xe3\x4f\x97\xc2\x8e\xc9\x7f\xbe\x54\x76\x2c\x05\x4b\x66\xb4\x75\xe7\xd5\x24\xb6\x1d\xb0\x7e\xc8\xfc\x76\x4c\xc8\x3a\xcd\xbd\x4e\x73\x1b\x9c\x5b\xdb\x38\x15\x98\xb4\x4e\x73\xbf\x2c\x33\x67\x89\x34\xb7\xb5\xa1\x57\x2a\x3a\xce\x98\x2c\x8f\x4d\x7b\x67\xc1\x55\xc9\x7e\x7b\x76\x9f\xca\x09\xf0\x4c\xbf\xa7\xae\x40\x7e\x99\x69\xac\x78\x02\x16\xae\x10\xce\x30\x73\xad\xdd\xd7\x19\xf1\x27\x3e\x2f\x91\x48\xa0\x79\xc4\x2f\x7e\xb7\xe0\x29\xbf\xb4\x97\xdb\x01\x28\x3a\xe8\x67\x7b\x43\x4f\x7f\xd6\xef\xf1\xba\xd8\xcc\xd7\x67\x3c\xcc\xa2\xd3\x7e\x25\x4e\x63\x79\xd3\x17\xad\xff\x2a\xc6\xf0\x12\x07\x70\x1d\xcb\x5b\xdb\xb9\x55\x98\xb4\x64\x2c\x2f\x11\xb3\x75\x4c\x6f\xd9\x3c\xd6\xf8\x49\xd4\xe7\x38\xf2\x1d\x31\xba\x37\xd3\x33\x3f\xab\x45\xc7\x7e\x04\xed\x3c\x7e\x99\x22\x9d\xdb\xba\x7a\xae\x4b\xa3\xe8\x2f\x99\xe9\x7a\x92\xe0\xd5\x02\xd1\x22\x5b\x65\xd8\x51\xba\x78\xcd\x70\x01\xc5\x58\xdd\x8f\x12\x93\xbe\xd6\xcb\x6b\xbd\xbc\x62\xbd\xbc\x56\xc9\x2e\x9e\xad\xa4\xe0\x6a\x05\x5a\x39\x53\x78\x55\x60\xd7\xe6\x2b\xab\xca\x34\xf2\xdc\xd6\xeb\x7a\xac\xb5\x5e\x7c\x21\x4c\x7a\xc2\x7a\xac\x59\x60\x76\x5d\x8a\xb5\xca\x52\xac\xd5\x45\x41\xb6\xa1\xef\x53\xd2\x4b\xa3\x20\xeb\xb0\xc8\x72\x61\x91\x23\xc9\xc7\x8f\x33\xae\x65\x77\x93\x82\xd0\xc7\x26\x07\x6a\x02\x0c\x7e\xbb\x76\x97\x85\x7b\xbf\xa8\x58\x8a\xcd\x9a\xd2\x48\xb2\x14\x99\x94\x18\x20\x46\x50\x00\x3e\xa2\xe3\xc0\x07\x7d\x04\xc6\x5c\xdf\x36\xe8\x51\x32\xc0\xc3\x31\x43\x4a\xb0\xf4\x3d\x7d\xa6\x07\xa3\x99\x42\x89\x96\x3b\xcd\xab\xc6\x7a\x3b\xfb\xa3\x6e\x67\xeb\xf0\xcb\x8f\x64\xeb\x3b\x4a\xaa\x2e\x60\x88\x78\x04\xbd\xc5\xef\xf0\x23\xb3\x9e\x0b\xd5\x55\x59\x03\x82\x39\x95\x55\xb3\xc6\x0b\xed\x17\xf3\x6a\xab\x48\x06\x6a\x95\xea\xaa\x6c\x9f\x45\x4a\x93\x66\x7d\x9f\xaf\x38\x69\xc6\xc8\xe5\xca\x93\x66\xdd\x57\x52\xa0\xe4\x86\xf6\x43\x96\x28\xcd\x48\x59\x17\x29\xad\x8b\x94\x0c\xce\xad\xad\x87\x0a\x4c\x5a\x17\x29\xbd\x2c\xc3\x61\x99\x22\x25\x7b\x5f\xac\xe4\x03\xe6\x2c\x80\xc7\x16\x2a\xe5\x01\x56\x29\x55\x22\xd9\x5e\x95\x8b\x95\x72\x3d\xd7\x17\x26\xa6\xcf\xd3\x6c\xb7\x0b\xd7\x44\xe5\xe6\x6c\xbd\x9d\xac\xab\xa2\x9e\xb8\x2a\x2a\x95\x41\x33\x22\x38\x7b\xbb\x60\x65\x94\xd9\xcf\xed\x81\x14\x05\x01\xb3\xbe\xcc\xd3\x87\x01\x57\xb1\x05\x98\x81\xc0\x9c\x9f\x58\x14\xfb\x2b\x75\xfd\xe6\x35\x7e\xe1\x3a\xb1\x62\x9d\x54\xea\x8d\xae\x2b\xa5\xd6\xc6\x76\x15\x26\x2d\x19\xaa\x4b\x05\x6d\x1d\xac\xfb\x9e\xb5\x52\xab\x50\xa6\x99\x6a\xa9\x19\xc8\x8a\xf5\x52\xa5\x6a\xb5\x42\xfb\x1f\xb2\x66\xaa\x38\xa6\xb6\x9a\xaa\xa9\x19\xfc\x75\xdd\xd4\x5a\x4b\x3f\x81\x96\x5e\x2b\x68\x17\xd7\x56\x53\x39\xb5\x0a\x1d\x9d\xa9\x9d\x2a\xb4\x79\x1d\xf5\x50\xa5\xfa\xb9\x42\xfb\x75\x05\xd5\x5a\x43\xbe\x10\x26\xad\x2b\xa8\xfe\x44\x15\x54\x46\xc4\x04\x4d\x60\xb0\xf2\x44\xf3\xe9\x04\x06\x63\xf5\x6e\x95\x99\x66\x3e\xa2\x4c\x80\x00\x4f\x24\xed\xb3\x11\x96\x4a\x40\x57\xea\xfe\xa3\xe6\xa2\x25\xf7\x1f\x99\x8f\x96\x20\x56\x9b\x93\xce\x41\x5c\xe7\xa5\xbf\x37\xd6\xeb\xbc\xf4\x93\x71\x6e\x6d\x62\x54\x60\xd2\x3a\x2f\xfd\xb2\x5c\xb0\xc7\xe5\xa5\x37\xd2\x51\x25\x72\x31\x85\x5d\x7d\x9d\xe1\x2b\xfd\xff\xe0\x98\x86\x21\x25\xf1\x2b\xf5\x9f\x73\x9c\x1a\x19\x33\xc5\x6f\x18\x03\xb7\x98\xf8\xc6\x9f\x11\x1c\x22\xe3\x4f\x8e\xbf\x99\x7f\x0a\x2a\x60\x60\xfc\x8d\x05\x0a\x13\xb3\xc4\x71\x9f\x63\xc4\xa4\xad\x22\xb0\xc9\x6a\x39\xde\xdc\x04\x8d\xc4\x22\xdf\x08\x13\x81\x86\x66\xd1\x37\xfe\x56\xa1\x95\xc2\xb9\xb8\x99\xfa\xa0\xc4\x24\x69\x03\x83\xe0\xc3\xc0\x64\x51\x99\x80\x7d\x50\xf4\x5e\xa2\x01\x62\x88\x78\x56\x66\xbb\xe0\x82\x4b\x17\x53\x80\x5a\x13\x7e\x4e\xea\x9c\xcc\x01\x6a\x26\xa1\x63\x85\x14\x36\x9f\x99\x8c\x3d\xec\x97\x76\x52\xdf\x32\x34\x75\x17\x9b\x60\x3c\x7f\x7a\x2b\xc9\xc0\x48\x72\xbd\xa8\x91\x81\xe7\x7b\x24\xe0\x82\x28\xd2\x7b\x82\xd8\x5c\x04\xb4\x69\xed\xf7\xa0\xa5\xa7\x06\x94\x85\x50\x74\xa5\xd9\x89\xea\x02\x87\x68\x1e\x98\x90\xfa\xca\xdd\x5a\x16\x8e\x7a\x1f\xff\x0c\x78\x6c\x17\x61\x4a\xae\x90\x90\xda\x82\x97\x2d\x6d\x6c\x2e\xec\x31\x0b\x1e\x37\x69\x63\xe6\x58\x45\x0e\x1c\x8f\x3c\x8f\x8e\x49\xa9\xce\xf1\x02\x8c\x88\xe8\x59\xf8\xc5\xef\x38\xf2\x18\x2a\x9b\xbb\x59\xdf\xf9\xf3\x67\x42\x2c\x47\xfd\x04\x45\x01\x9d\x86\x88\x88\x73\xaa\x77\xa0\xa4\xbd\x8f\xa5\x02\x0f\x31\x81\x82\x1a\x22\x13\x63\x36\xbd\x50\xc9\x6c\x4b\x87\x86\x30\x8a\x30\x19\x9a\x03\x66\x0f\x7a\x54\x3d\xc6\x7c\x0d\xd9\x10\x59\x3e\x45\xde\x30\xae\x6e\x42\xdb\xd0\x28\x41\xd5\xb5\xdc\x3c\xc4\x2a\xf6\xce\xa1\xb2\xe1\xe2\x8e\xfe\x38\x9b\x00\xeb\xd7\xf7\xf5\x37\x0e\xee\x29\xbb\x0d\x28\xf4\x39\x10\x54\x5d\xe0\xa8\x8e\x6b\x78\xf6\x45\x1b\x0e\x6d\x30\x67\x07\x5c\x7a\xc3\x4a\xcf\x31\x95\x0b\x5a\x11\x2b\x1e\x41\x6d\x6a\x22\x3c\x03\xdd\x66\xf1\x46\x39\xe5\x99\xdf\xea\x7f\xa2\xcd\x16\xb9\xac\x39\x45\x19\xa8\x1d\x7d\x3c\x8b\x91\xb2\x0d\x44\x2c\x3f\x4e\x5a\xf6\xcb\x91\x46\xcb\x1d\xef\xa9\x65\x36\xf2\x20\xd0\x4a\x3a\x67\x61\xd6\x35\x70\x15\x1e\xe2\x59\xb3\x74\xce\x20\xf9\x9f\x2a\xcd\xf5\x8f\x09\x2b\xfc\xed\xa9\x62\xd3\xa3\x10\x63\xcd\x57\xc8\x18\x9c\x66\xbe\x28\xdb\x2f\x6f\x26\x67\x26\xd4\xa4\x7d\xa1\xa9\xb5\xcc\xda\x78\x6b\xe1\xa6\x61\xfb\x8b\x64\x47\xf1\x86\x68\xad\xa4\x9f\x68\x20\x17\x8f\xb6\xac\xd5\xb9\x2f\xed\x09\xeb\x83\x60\x12\x82\x5a\x59\x1a\x26\x38\x23\x5c\x40\xe2\xa1\xc6\x32\x32\x5a\xb8\x53\xa7\x13\xf1\x2a\xfe\x8d\x81\x38\x12\xeb\x19\xf3\x92\xb6\x29\x10\xe9\x57\xf6\x2c\xea\x8d\x57\x0d\x7d\x89\x86\x98\x0b\x36\x5d\x31\x4b\x14\x70\x90\x00\x7f\x02\xde\xe8\xc6\x80\x25\x23\xae\x8a\x4b\x89\x2c\xa9\xa3\x84\x96\x24\xd9\x87\x0b\xdd\xaa\xf8\x28\x7b\x4c\xb2\x44\xd9\x2e\x69\x60\x4d\x60\x30\x76\xf8\x33\xa6\x12\xcd\x1f\x83\x2c\xc2\x36\xa9\x1c\xcd\x1e\xee\xb4\xd1\x36\xd7\x75\x66\x3d\x57\x3f\x8d\x59\xcb\xba\xa0\xf9\x6b\xae\x67\xac\xce\xee\xf5\x57\x02\x8a\x8c\x83\x61\x71\x05\x91\x71\x68\x4a\x97\x34\xc7\x34\x08\x64\x1a\x8f\x0c\x41\x7f\xea\x1e\x21\x0e\xcc\x9a\x5e\x82\x6b\x7e\x54\x55\x62\xb5\xad\xdb\x06\xec\x9e\x00\xbd\x24\xa5\x91\x6f\x16\xa5\xa5\x85\x1f\x00\xaa\xb8\x35\x88\x02\x48\x90\x71\x16\x57\x57\x48\xd4\x96\x59\x5c\x25\x84\x17\x18\x5a\x26\x4f\x96\xd8\x87\x35\xe4\xef\x85\xdc\x95\xe2\x44\xd9\x94\x71\xab\x45\xc1\x5a\x2c\xea\x9c\x00\xc8\xb9\xdc\x8b\x50\xa1\xa4\x37\x13\xf2\x37\x23\x09\xd5\x85\x69\xd5\xe6\xd0\x22\x54\x3c\x66\x1e\xaf\x62\x79\x75\x12\x65\xea\xa7\x85\x08\xb3\xed\x96\x85\x23\x29\x4e\xcb\x64\x61\x43\x66\xb1\xab\xfd\xdc\x2a\xd0\x78\x7b\x3c\x82\x84\xa0\xa0\x44\xd7\xf9\x68\x00\xc7\x81\x90\x6f\x61\x3f\x40\x05\x1a\x30\xfe\x68\x33\xfc\x04\x71\x69\xdd\x2f\xaa\x4d\xb5\xda\x34\x61\xd3\x28\xb2\x14\xab\x1f\x57\x21\xd8\xc3\x2d\x3a\x0e\xe4\x1c\x0f\x89\xb9\xd7\x25\xef\xac\xc1\x94\x6a\xb4\x5b\xcd\xc7\x70\x00\x71\x90\x47\xd9\x86\xe2\x67\x6a\x29\xea\x52\x74\x26\x58\x9a\xfe\xd9\x86\xd6\x87\x8c\x54\x9b\x76\x52\x69\x48\x55\x5a\x77\x26\xd2\xda\xec\xe9\x41\x1d\x19\x31\xbe\xe4\x7e\xb0\xdf\xe5\x87\x49\x68\xa6\x78\x96\x09\x66\x81\x51\x9c\x2e\xa6\x0c\x2e\x79\xb8\x9b\x65\x96\x5b\x1c\xdb\xd9\x4c\xc1\xa9\xef\xbd\xc4\x58\xab\x8a\xe6\x3c\x8b\x35\xc5\x77\xc6\x21\x13\xf4\x2b\x23\x40\x5e\x66\x1e\xca\x96\x6a\x9b\xe5\x23\x18\x21\xeb\x75\xc4\xa8\x87\x38\x37\x7f\x6f\x57\xbe\xd6\x41\xf9\x11\x24\x7e\x60\xc7\x50\x2d\x15\x64\xcb\x85\xc3\xc2\x70\x49\x85\xb4\x30\x5c\x53\xdf\x93\xa0\xed\x58\x98\x3f\x0b\x47\xf5\x82\x38\x1e\x65\x7d\x55\x8b\xbd\xa7\xb6\xaf\x65\x4d\x9a\x1c\x7f\x13\x34\xe6\xf7\xb0\x15\xd9\xbc\xa9\x8e\xf5\x5e\x3a\xa3\x0e\xe2\xaa\xc2\xca\x87\xe9\x4c\xb0\x06\x57\x2a\x23\xe7\x52\xa0\xd9\xdd\x2c\x63\xe8\x2d\x67\x94\x59\x06\xcf\x82\x7d\x2d\xc5\x93\xc5\xee\x39\x8c\xb8\x02\x62\x16\xdc\xa6\x93\xa4\x61\x6f\xa2\xa3\x30\xee\x1d\x3b\x9b\xce\xd1\x4f\x12\x3d\xc7\x44\xec\x75\x1c\xbb\xd3\x8b\xb5\x1c\x57\x60\x32\x3e\x8b\xad\xb8\x0a\xc1\x5d\xb0\xb7\xdb\xb6\xfc\x13\x18\x95\x35\xb7\x31\x09\xae\x67\xbf\xae\x9f\xf5\xa6\xe5\x17\xa7\x23\xfa\xf7\xfa\x0c\x85\x4b\x14\x31\xc4\xe5\x88\x56\x89\xac\x3a\xd3\xc2\xc7\x51\x44\x99\x40\x3e\xe8\x4f\x95\xc3\x7a\xf4\xf1\x2c\xee\x98\xcb\x13\xe4\xf7\x36\x90\xdf\xdf\xf4\xab\x78\x61\x67\xde\x6a\x7a\x57\x09\xf1\x2b\xa7\xa4\x67\x81\x7d\xa6\x1c\x6e\x76\xcb\xcd\xcd\xc7\x05\x0c\x51\xfe\x20\xa3\x1c\xa5\x51\xa6\x00\xcc\x0f\x05\xda\xd2\x2e\xf6\xd1\x6d\x1e\x39\x52\xbc\xd3\xe7\xc4\xd8\x2e\xc9\x8b\x1b\x2d\x32\xd6\xaa\x16\x4c\xd6\xb4\xc8\x22\x57\x86\xf7\x91\xf9\x67\x0e\xf9\xca\x3c\xc2\x1e\x25\xbd\x6c\xaa\x3a\x37\xd8\xa7\xcb\xf3\x38\x75\x23\xdb\x2f\x3f\x5a\x00\xfb\xf3\xe6\xe3\x5c\x35\x49\xef\x39\x83\x02\x0d\x29\xc3\xdf\x90\x3d\xe4\x63\xe7\xa5\x58\x68\x60\x04\xfb\x38\xc0\xf9\xc5\xe1\x3a\xcd\x69\x34\xce\x2b\x21\x4f\xce\xf7\x77\x45\xd6\x5d\x52\x54\xa4\x41\x93\xe7\x48\x29\x9c\x24\x50\xad\x2e\x98\xf3\x20\x31\x6f\x97\x9b\xe8\x62\x3b\x04\x60\xce\x8c\xcc\x41\x4b\x17\xcc\x00\xa3\xc0\x77\xcb\x42\x4e\x03\x01\x53\xe9\xfd\x38\x04\xe4\xb7\xad\x3f\xc1\x7e\x2e\xc9\x2c\x0c\x92\x67\xca\xbb\x5f\xd9\x1c\xca\x9e\xcd\x5b\xc0\xc9\xac\x98\x98\xa8\xe4\x25\x42\x42\xa8\x80\xb9\x04\xa1\x9b\x5d\x0e\x56\x15\x0a\x71\xd1\xec\xb8\xb7\xd2\x92\x95\xec\x48\x9f\xcc\xe9\xe1\xb6\x3a\x9c\x76\x87\xb2\x3c\x24\xf8\xa2\x24\xfe\x73\x38\x61\x2e\xd1\xc8\x1a\xcb\x69\x95\x01\x22\x30\x8d\xf6\x2c\x5e\x20\xb0\x92\x32\x98\x31\xaf\x50\xfa\x92\x41\xf9\x13\xb7\x7f\x38\x85\xb2\x21\x24\x98\xc3\x38\xcf\xb2\x10\xac\x0f\x46\xdf\xc7\x95\xc1\x94\x22\xb9\x1c\x0c\x1b\xb9\xd2\x69\x94\xc3\x95\x4c\xe5\xd2\xc5\x1c\x72\x7e\x96\xa9\x60\xc9\xe1\xff\x3d\x90\x33\x27\x7e\x09\x24\xcb\xb2\x75\x47\xce\x63\xa4\xba\xfc\x2f\x73\x9f\xc4\x72\xc1\x9f\xf2\x93\x22\x8b\x57\x8a\x3a\x8b\x8d\x0a\x39\x57\xa4\xea\x62\x45\x57\xc0\x30\x03\x55\x37\xd3\xc8\x9c\x43\x3d\xdf\x85\x81\x55\x74\xdd\x9f\xc0\xa2\x30\x8e\xc5\x14\x30\xa1\xba\xa0\xaf\xd8\x63\x5e\x0c\xff\x47\x86\x10\x5d\x46\xc2\xa2\x8e\x73\xf5\x30\xa3\xf9\x65\xe1\x35\x08\x00\x7a\x88\x30\xcb\xc5\xbb\xe5\xf3\xaa\xac\x18\xb8\x14\xa6\xb0\xf6\xf5\xe4\x59\x66\xff\xa9\x55\xd0\x17\x46\x92\x02\x2c\x16\xa1\x71\xdc\xf3\xae\x3f\x68\x02\xa4\x14\x67\xef\x8a\x4a\x39\xab\x6f\x8c\x9a\x75\xcd\x79\x91\x67\x27\xd2\xa3\x66\xc8\xa3\x6c\x76\xc9\x47\xc6\x2f\x72\x30\x30\x73\x09\x94\xe3\x50\x9c\x79\x0a\x41\xe3\x60\x9c\x8e\xc8\xfe\x04\xb9\xfd\x43\xe3\x70\x88\x00\x26\x3e\x7a\xc8\x41\x1f\xc0\x80\xa3\xea\x58\xe6\xcf\xaa\x64\xcf\x46\x68\xbb\x17\xd4\xe2\x2a\x44\xf3\x50\x84\x46\xda\x38\xc3\x51\x8a\xf4\xc5\x38\xec\x23\x26\x59\xa9\x54\x13\xc0\x04\x20\xe8\x8d\x4c\xa2\x57\x48\x46\xf6\xf0\xc6\x8c\x8c\x66\x53\x13\x12\xdf\xfc\xe7\x54\x64\xff\x49\x7d\xda\xab\xf8\x38\xb3\xae\x65\x53\x9d\x40\x7f\x0a\x3c\x86\x05\x62\x18\x36\x94\x84\xf0\x29\x11\xf0\x41\xc7\x5d\x30\x4f\x45\x0d\x60\x6e\x20\x14\xe2\x00\x32\xe9\xfd\x8a\x4c\x17\x04\x6e\x12\xc0\x37\xc0\x0b\xe0\x98\xab\x20\x1e\x24\xe0\xea\xd7\x73\x9d\x0c\x08\x11\x11\xa9\xe7\x7b\x2a\xf9\xa6\x18\x9d\x38\xd6\xaa\xbf\x0e\x6d\x40\x32\x9d\x81\xb5\x7c\xc4\x1b\xed\x40\xf3\x14\xce\x5b\xca\x12\xd6\x6d\x49\xc4\x98\xba\xcd\x51\xea\x6a\xc3\x83\x94\xec\xe6\xe6\x00\x62\x84\xb0\x56\xf0\x5b\xd2\xa6\x53\x23\x0d\x68\x10\xd0\x7b\x4c\x86\x31\x61\x71\x51\x9c\x7c\x6e\x6e\x6e\xf8\x5d\x60\x39\x84\x00\x72\xcf\xfc\x9e\x36\xbe\x5e\x1c\x09\xd0\x83\xc4\xef\x25\x8a\xe1\x31\x28\x6d\x25\x40\x8a\xf1\x3b\xd3\x8c\x35\x67\x98\x6c\x0a\x9d\xef\xf7\x91\xbf\x05\x28\x03\x58\xb7\x51\x12\x07\x30\x07\x28\x8c\xc4\x74\x4b\xbe\x4b\xd5\x96\xae\xda\xe2\xe3\x40\x70\x00\x99\x35\x7f\x12\x9b\xc6\x4c\xae\xa3\x80\xfa\xc8\x3a\x49\x9b\x97\xf5\x8c\x28\x9b\xe2\x9e\x90\x56\x2b\x58\xa1\x7a\x09\xc7\x00\x1e\xbb\x0a\xb9\x98\x06\xa8\xab\x76\x35\xad\x2b\xd4\x55\x99\xee\x15\x96\x2e\x30\xd5\x28\x5d\x50\x86\x2c\x94\xaf\xac\x39\x2b\xea\x7e\x84\x18\xb2\x96\x53\x3a\xa4\xb5\xaa\xc0\x91\x94\x13\xe4\xc7\xab\x43\xea\x25\x05\x4e\xe3\x25\x27\xe7\x46\x72\xe9\x66\x0b\xdc\x18\x24\xc8\x3f\x63\x69\x91\xff\x54\x91\xd3\x9b\x2d\x00\x89\x0f\x6e\xe2\xc0\xf6\x4d\xba\xd0\x92\x21\xf4\x41\x29\xca\xf4\xa4\xdf\xfc\xcf\xdf\x65\xdf\xd7\x37\x4a\x6c\x6e\xce\xcf\x7e\x39\x75\xf4\xf1\x28\xf9\x3a\x26\x9e\xc0\x13\x94\xed\x7f\x74\x71\x72\xa3\x87\xfc\x70\x79\xd3\x00\x3f\xd1\x7b\x34\x41\x6c\x0b\x4c\xe9\x58\x29\x06\x49\x39\x04\x21\x7c\xc0\xe1\x38\x94\x3c\x68\x35\x53\x70\x94\x28\x5a\x61\x42\xa9\x12\x0b\x83\xfd\xa7\x33\x39\x73\xad\xce\x4c\xde\x48\xdf\xfd\x20\xf9\xa6\x24\xee\x06\xde\xf3\x3a\xbf\xe3\x75\x9d\x82\xd5\x48\xaa\x98\xab\x66\x0d\xb8\xd1\x75\x46\x37\x55\x97\xab\xbd\x56\x5f\x03\x1b\xbe\x02\x9f\x80\x7e\x6d\x17\x38\xa9\xee\xff\x8c\xea\xff\x72\x93\xa1\x4b\xb2\x71\x5c\x76\xac\xc9\x80\x7a\x14\xfd\xd3\x1e\x02\x32\xc1\xf5\x7b\x49\xd5\x92\x18\x07\xf8\x16\x49\xa4\xff\xd2\xde\xfd\x2e\x8a\x45\xa9\x4b\xf9\xd1\x9e\x16\x43\xdf\x40\xa1\xbe\x4b\x27\x1c\x8c\x20\x07\x11\x62\x21\xe6\x3c\xae\xc9\xe6\x08\x29\x91\xd2\x7c\x41\xbe\x21\x07\x17\x54\xa0\x46\x82\x9f\xde\x74\xd2\x73\xcb\x52\xe2\xe3\xaa\x16\xcc\x8d\xde\xc5\xea\x2b\x36\x1a\x94\xcc\x15\x28\x25\xb7\x02\x72\xec\xf1\x96\x7e\x01\x59\xb5\x57\x49\x4a\x6a\xcb\xa9\xb7\x8d\xf4\xea\x0b\x55\x6c\x94\xa0\x15\xdf\x7d\x61\x02\x45\x5d\xd0\x57\x6f\xe3\x97\xfa\x8f\xb7\xb1\x49\xfe\xf3\xe7\xeb\x0d\x73\xc4\x91\x10\x91\x84\x6e\x53\x9b\x2d\x08\x74\xde\xe5\x90\x89\x50\x6a\x46\xd7\xde\x4f\xad\xdf\x07\xb6\x2c\x82\x72\x00\xd8\xef\x82\x80\x0e\x7b\x1c\x93\xdb\x5e\xb3\xd1\x9a\x7d\xd0\xe7\x40\x2c\x48\xb3\x6f\x0b\x9d\x31\x51\x45\x41\x7c\xdb\x1c\xa4\x96\xc1\xff\x9c\x0e\xc1\x15\x26\xb7\xb3\xd7\x89\x9b\x05\x6a\x56\x6b\x57\x32\xb1\x9e\xd5\x04\x76\x26\x2b\x0b\x39\xcd\xb5\x2d\x89\x7f\x23\x22\xc3\x14\xa3\x7c\x32\xad\x0e\xb8\x39\x5e\x51\x2a\xab\xae\x8a\xca\x7a\xd9\xa2\xb2\xba\xab\xa8\x2c\x9f\xa0\x29\x3e\x84\x13\x86\x79\xd7\x30\x5d\x6a\xe9\x6d\x2d\xc9\x23\xb0\x08\xf4\x0c\x38\xdd\x45\x47\xb8\xbd\x2c\xe0\x0e\x40\x38\x0e\x04\xee\x05\x98\x38\x4f\xbf\xcf\xca\x53\xcd\x35\x6f\x37\x30\x26\xef\xbd\x84\x05\xce\x31\x71\xb5\x8c\x11\x2f\x6f\xa3\x68\xe8\x53\x1a\x20\x48\x1c\xdf\x1f\xea\x43\x46\xc7\x51\x17\xd4\x10\xf1\x23\x8a\x89\xc8\x9f\x87\xe2\x23\x7a\xdf\x83\x41\xf0\x78\x72\xae\x46\xf4\x5e\x6e\xf8\xc5\xc4\x94\xb5\x78\x24\x29\x82\x46\xd8\x9b\x93\x85\xa7\x61\x28\x0d\x05\xb9\x3d\x09\xe4\xcf\x8e\x7f\xe8\xdd\x53\x01\xd0\x11\x1f\xb7\x08\x5d\x17\x37\x28\x4e\xb8\xa4\x68\xab\x55\x67\xe3\xcc\x05\x8a\x1e\x1f\x0c\xcb\x44\xe7\xd3\xa7\x5e\x2a\xc8\x31\x4c\xc2\x11\x13\x3d\x65\x35\x16\xb5\x29\xf6\x2b\xf3\xcf\x91\xef\xab\xd2\x99\x31\x17\x34\xd4\xc6\x68\x62\x8e\x78\x54\xd9\x27\x22\xde\xfa\x63\x83\x37\x44\x9c\xeb\x40\x00\x10\x0c\x12\x8e\x45\x36\x37\x9a\x3e\xf3\xc9\x91\xcf\x1c\x5a\x72\xf4\x5c\x27\xf6\x5e\x6c\x74\x6b\xa4\x05\x95\x1e\x29\xf4\x7d\xe4\x97\x82\x8a\x85\xe3\xad\xec\x54\xde\xb0\x58\x48\xcc\xa7\x20\x43\x57\x8a\xbd\x66\xa8\x89\x7e\x15\x94\x7f\x53\xc9\xba\x47\xa3\x5c\x94\x22\x34\x9f\xfa\x5c\xac\x92\xdc\xe1\x1c\x9c\xcf\x94\xb8\x6a\x6e\x83\x23\x4f\x64\x83\x68\x79\xec\x9d\x0a\xbe\x1a\xe6\x75\x6b\x75\x38\x1b\xcd\x19\xa3\xca\x0a\x44\x0f\x82\x41\x6f\xb1\x25\x78\xaa\xfb\x00\x18\x0b\xeb\x80\xd1\x50\x4d\x7e\x9f\xfa\x59\xad\x91\x3e\x7f\xfc\xe5\xb3\x0a\x59\x8c\x31\x4a\x58\xfc\x54\xa2\x66\x89\xc1\xf7\x92\xb5\x11\xe4\xbd\x11\x82\x3e\x62\xbd\x01\x0e\x04\xca\x55\xd4\xa6\x8f\x35\xc7\x6f\x55\x63\xd0\x87\x5c\xba\xff\x3a\xb4\xa0\x0b\x25\x3d\x35\xef\x94\x20\xa0\xe1\x3e\x52\xf8\xdc\x05\x0d\x85\x78\x49\xd9\xd3\xe3\xc6\xce\x2e\x4d\xf2\x6d\xe5\x8a\x2d\x39\xf1\x1e\x77\xbe\xc8\xd7\x32\xd8\x4f\x2c\x13\x3f\xe9\xa1\xe6\x37\x5f\x9d\xac\x3a\xca\x2c\xf2\x68\x41\x9e\xa0\x16\x4f\xd4\xf7\x17\xd7\x9c\x24\x55\x13\xd9\xd4\x05\xac\xec\xfb\xbd\x9f\x9e\xd3\xa1\x99\x75\x9a\x73\x36\x22\x73\xbe\xff\xd8\xbe\xee\x02\xd8\x69\x29\x50\x3b\xec\xf3\x49\x93\xef\x0b\x82\xf6\x87\xcd\xf6\x70\xb4\x3b\xec\x18\xee\x4f\xee\x5c\x91\xd1\x67\xaf\xcf\x06\xac\xd9\x6c\x47\x03\x72\x3b\x6a\xda\x03\x24\xd7\xaf\x80\x1a\x67\x13\xaf\x0e\x3d\x4f\xd4\x5b\x7b\x6d\x34\x68\xfb\x07\xf5\x66\xbb\x79\x58\xef\xb4\x5a\xfb\xf5\x83\xce\x5e\xbb\xee\x0f\xf6\x76\xbc\x76\xb3\xbd\xeb\xb5\xf7\x1c\x50\xe2\xab\x59\x40\xad\xdf\xea\x74\xfc\xc3\xc3\x56\xbd\x79\x80\xfa\xf5\x4e\x67\xbf\x5d\x3f\x40\x5e\xab\x8e\xfa\xcd\x9d\x8e\xb7\x77\xd8\xde\x69\xf5\xcd\xfe\x63\x16\x74\x41\x6d\x40\x69\xdd\x85\x6f\xe3\x16\xf2\x06\xf4\x42\xd4\xf0\x68\xd8\xed\x74\x76\x6a\x19\x77\xcc\x79\x5e\xc9\x20\xbf\x79\x7b\x10\x90\x61\x73\xa7\xc5\xd1\xe1\x5d\x05\xf2\x51\xb3\xbd\xdb\xde\xdb\x45\x75\x78\x70\x00\xeb\x9d\xce\xa0\x5f\x3f\xe8\xec\x36\xeb\xc8\x6f\xb6\x9a\xa8\xbf\xd7\xf7\x76\xbd\x32\xf2\x7d\x6f\x17\x1e\xb4\x0f\x0f\xea\x7d\xe4\xef\xd7\x3b\xed\x36\xaa\x1f\x1c\x76\xf6\xeb\x83\xbd\x81\x0f\xf7\x0e\xdb\x87\xed\xc1\x20\x4f\x7e\x1f\xb2\x98\xfc\x76\x38\xf0\x60\xb3\xd9\x16\x87\x77\xfb\x7c\xd8\xe0\xac\x88\xfc\xe4\xec\x4e\xd6\xef\xce\x9f\x02\x02\x35\xb7\xd3\xef\x3c\x8f\xe5\x72\x5d\x67\xbe\x97\x19\x5b\xd2\x4f\xea\x67\xf2\xdc\xd7\xd8\xd7\x51\x93\xbb\xd5\x87\xf6\xfd\xc8\x33\xaf\xdb\x1e\xea\x16\x4d\xb3\xab\x39\x49\xa8\xd6\xae\xae\x2f\xcf\x2e\xde\xd9\xbe\x89\xd3\x0e\x9d\xf5\xf8\xf9\xea\xc3\x45\xe6\xd2\x8c\xd8\xa9\xcf\xa5\x43\x4b\x1d\x8c\x38\xbc\xa3\xbe\x4a\xb5\x9a\x77\x4f\x93\x60\x98\x6a\xa2\x4c\xd6\xa2\xe3\x4c\x99\x73\x90\x2a\x9e\xd7\x4b\x4e\xa9\x99\x43\xfb\x08\xfa\xbd\x00\x09\xa9\x03\xee\xc6\x28\x4b\xa6\xe2\xae\x14\xb8\xe0\x2e\x13\x6d\x2a\xff\x65\xcc\xa2\xc8\x95\xe3\x7a\xce\x05\x34\x50\x41\x55\x61\xf2\x13\x41\x35\x3b\xbc\xd3\x80\x11\x6e\xd0\x08\x11\x3e\xc2\x03\x21\x65\x7b\x3b\x62\x74\x80\x03\xe4\x9a\x5d\x50\x8b\x1d\xfc\xba\xd5\x68\x81\x9b\x57\x8b\x68\x96\x1d\x1c\x74\x3f\x03\x31\xc5\x17\x88\x3a\x02\x85\xb5\x56\xd3\x50\x04\xf1\x4e\x92\xb9\x1e\xb1\x3c\xb6\xa6\xaf\x0d\xdd\xb6\xe0\xa8\x4b\xeb\x40\xed\xf8\xc3\xc5\xc5\xe9\xf1\xf5\x87\xcb\xfa\xfb\x77\xef\xaf\xeb\x56\x93\xf8\xaa\x3a\x50\xbb\x9a\x12\x6f\xc4\x28\xa1\x63\x0e\xa0\xda\xc5\x01\xe6\x80\x50\x91\x56\x7e\xeb\xd8\x3d\xe4\x53\xe2\xbd\x96\x8a\x21\x7f\xdd\x46\xe6\x2e\x3b\x50\x6b\xe1\xcf\x67\x38\xbc\x7b\xe7\xb1\x93\xf1\xf9\x5e\x0b\x7e\x7a\x38\xfb\xc7\xdd\x9b\xeb\xbb\x8b\x4b\x38\xe3\xd2\x99\x8e\x85\xff\x3a\x46\x6c\x5a\x81\x53\xed\x15\x71\xaa\x3d\x97\x51\x6d\x07\x9f\xfe\x63\x08\xc0\x5b\x75\xb8\x59\xda\x7e\x11\x64\x1c\x59\x99\xa0\x2e\xf8\x44\xa4\x12\x97\x5f\x55\xb8\x47\xc7\x7a\xe2\x72\x2b\xae\xee\x9e\x80\x11\xee\xe9\x90\x68\x7c\xee\xb7\x0b\x72\x18\x74\x17\x18\x2f\x2d\xd1\xf7\x68\x30\x0e\x89\x36\x4d\xe5\x48\x71\xa8\x1f\x6c\x62\x7f\xb3\x01\xae\x5c\xed\x54\x4e\xcc\x1c\x4d\xee\xc2\x94\x6c\xc5\x99\x6a\x2f\xa0\x63\xbf\x17\xe7\x53\x58\xf2\x56\x1f\xd5\x6b\x80\x5f\x75\x5e\x43\x4f\x64\x17\x60\x1f\xbc\x06\xad\xf6\x4e\xa1\x54\x04\x9f\x4f\xde\x8d\xa7\xfd\x33\x76\x4a\x1e\xd8\x11\x0a\xf7\xdb\x9d\xe1\xdd\xed\x2d\x3e\x99\x24\x52\x91\xbd\x1d\xd5\x25\x09\x9d\x66\x67\x25\x92\xb0\x3f\x4f\x10\xf6\x1d\xeb\xa5\xca\x15\xab\x33\x62\x9c\xb7\xa1\xbb\x48\xda\x7f\x3e\x82\xd2\xd4\x97\x0a\x9b\x61\xff\xf5\x66\x0b\xff\xb2\xe3\x8f\x7f\xfb\x72\x36\x99\xec\x7e\x99\x9c\x07\xd3\x6f\xad\xf0\xdd\xe5\xce\xcf\xd3\xbb\x8b\x4d\xa5\x1a\x06\x74\x4c\xfc\x92\xc5\xff\xe5\xc3\xfe\xb0\x3d\xdc\xfb\xe9\xda\xff\xf4\xcb\x27\xd8\xbe\xe5\x3f\x1d\xb4\x6f\x7f\x3d\xd9\x99\x26\x9c\xc9\xde\x14\xec\x54\x8d\xad\xd5\x68\xc6\xd6\x5c\xc5\xd8\x72\xb0\x25\x5d\xc6\x13\xc4\xf0\x60\x0a\x7e\xfe\x7c\xad\xef\x1f\xee\x82\xcb\xd8\x5b\x01\x70\x2c\x46\x94\xe1\x6f\xc9\x25\x4d\xb7\x88\x54\xe3\xcf\xce\xa7\xd1\xe9\xe8\x3e\xfc\xfd\x4d\xf4\xf9\xe3\xe0\xac\x1d\x5c\xa0\xdb\xc8\xef\xfc\xe3\x24\xe1\xcf\xa1\xdc\xcb\x8e\x29\x19\x04\xd8\x13\x15\x78\xb5\xb3\xb7\x12\x5e\x99\x60\xdc\xbc\x32\x5b\x98\x22\xa4\x8f\x04\x69\xcd\x83\x39\x80\x81\xb2\x8d\xd4\xc9\x95\x42\x3e\xec\xdd\x7e\x69\x7e\xc2\xa7\xb7\xdf\x6e\x7f\x3f\xfe\xf6\xf9\x23\x3a\x6b\xd3\x2f\x68\xe4\xef\x9c\xc6\x6c\xc8\xdf\xfb\xeb\x22\xfd\x70\x25\x94\x1f\xce\x23\xfc\xd0\x29\x23\xe9\xcf\x36\x20\x7b\xd0\xdc\x94\xa3\xd3\xf3\xc9\xdb\xc3\xaf\xef\x7f\xfd\xb2\xf7\x65\x38\x1a\xbc\x3f\x1c\xbe\xbb\xe4\x3f\x4d\x4e\x3f\xcf\x68\xad\xac\x2c\x9e\x8f\x62\x73\x17\x54\x63\xce\xee\xf5\x00\xd2\x3a\xe0\xd2\x6f\xfa\x70\xfc\xbe\x7e\xfa\x7b\xfd\xb0\x1b\x5f\x02\x22\x97\x90\xbe\xea\x23\x6d\x83\x1e\x44\x3d\xde\xfb\x60\x84\xeb\x2d\xfc\xd0\xdc\x09\x88\x1f\x84\x77\xcd\xbb\x81\xb7\xcf\xb1\x80\xbb\x3c\xf8\x3a\x39\x30\x9d\x10\xb9\xa1\x25\xb7\x2b\x4a\x3e\xb4\x86\xbb\xfe\xc1\xc1\x5d\x33\x60\x9e\x3f\xe9\x0c\xf7\x61\xd0\xdf\xe7\xc1\x60\x48\xbe\xee\xf8\xa3\x3e\xff\xfa\x97\xff\xf7\xd7\xd3\xdf\xaf\x2f\x8f\xc0\x7f\x69\x8a\x1b\x0a\xe3\xd7\xd8\x47\x44\xc8\x39\x33\x23\x08\x98\x83\xcd\x4e\xb3\xb3\xb9\xa5\x78\xa1\xfe\x3c\x3e\xff\x74\x75\x7d\x7a\x79\xa5\x99\x21\x3f\xaa\x4c\xf8\x6c\x62\x41\x0a\x48\xb5\x6f\x0d\x77\x29\xdb\x6d\x4e\xf0\xb8\xb9\x4f\x91\x9c\xb6\x11\xbb\xf5\xda\x7b\xfe\x70\x20\xbe\xb6\xa0\xb7\x69\x6e\xb2\x71\x90\x40\xf5\x2a\x25\xc2\xd0\xb7\x7f\x2b\xd1\x27\xd7\xfc\x33\x9b\xee\x11\x7e\xd7\x6f\xf3\x8b\xf0\xed\xd7\xdd\xfe\xef\xd1\xc9\xfe\x31\xac\x6d\xfc\x5f\x00\x00\x00\xff\xff\xa2\x8b\x55\x65\x78\xd6\x00\x00") +var _connector_mgmtYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x7b\x73\xdb\x38\x92\xf8\xff\xfe\x14\xf8\x29\xbf\x2d\xef\xde\x45\xb2\x24\xcb\x2f\xd5\x65\xab\x9c\xd8\xc9\x78\xc6\x71\x32\xb6\x33\x99\xec\xd6\x96\x0c\x91\x90\x84\x98\x04\x68\x00\x54\xa2\xec\xde\x77\xbf\x02\xc0\x07\x48\x82\x14\x29\x2b\xb6\x33\x23\x56\xed\x4e\x4c\x02\x8d\xee\x46\xa3\xd1\x2f\x40\x34\x40\x04\x06\x78\x08\x76\x3b\xdd\x4e\x17\x3c\x03\x04\x21\x17\x88\x19\xe6\x00\x72\x30\xc1\x8c\x0b\xe0\x61\x82\x80\xa0\x00\x7a\x1e\xfd\x02\x38\xf5\x11\x38\x3b\x39\xe5\xf2\xd5\x2d\xa1\x5f\x74\x6b\xd9\x81\x80\x08\x1c\x70\xa9\x13\xfa\x88\x88\xce\xd6\x33\x70\xec\x79\x00\x11\x37\xa0\x98\x08\x0e\x5c\x34\xc1\x04\xb9\x60\x86\x18\x02\x5f\xb0\xe7\x81\x31\x02\x2e\xe6\x0e\x9d\x23\x06\xc7\x1e\x02\xe3\x85\x1c\x09\x84\x1c\x31\xde\x01\x67\x13\x20\x54\x5b\x39\x40\x84\x1d\x05\xb7\x08\x05\x1a\x93\x04\xf2\xd6\x33\xd0\x0a\x18\x9e\x43\x81\x5a\xcf\x01\x74\x25\x15\xc8\x97\x8d\xc5\x0c\x81\x96\x43\x09\x41\x8e\xa0\x6c\xe4\x4f\x7d\xd1\x8e\x5a\x76\x16\xd0\xf7\x5a\x60\x82\x3d\xb4\x85\xc9\x84\x0e\xb7\x00\x10\x58\x78\x68\x08\x5e\xc5\x1d\xc0\x15\x62\x73\xec\x20\xf0\xda\x43\x48\x80\xb7\x90\xc0\x29\x62\x5b\x00\xcc\x11\xe3\x98\x92\x21\xe8\x76\x7a\x9d\xee\x16\x00\x2e\xe2\x0e\xc3\x81\x50\x2f\x97\xf4\xd7\xf4\x5c\x22\x2e\xc0\xf1\xfb\x33\x89\xa6\xaf\x3e\x80\x04\x51\xde\xd9\xe2\x88\xc9\x41\x24\x56\x6d\x10\x32\x6f\x08\x66\x42\x04\x7c\xb8\xb3\x03\x03\xdc\x91\xcc\xe6\x33\x3c\x11\x1d\x87\xfa\x5b\x00\xe4\x10\x78\x0b\x31\x01\x7f\x0d\x18\x75\x43\x47\xbe\xf9\x1b\xd0\xe0\xec\xc0\xb8\x80\x53\xb4\x0c\xe4\x95\x80\x53\x4c\xa6\x56\x40\xc3\x9d\x1d\x8f\x3a\xd0\x9b\x51\x2e\x86\x87\xdd\x6e\xb7\xd8\x3d\xf9\x9e\xf6\xdc\x29\xb6\x72\x42\xc6\x10\x11\xc0\xa5\x3e\xc4\x64\x4b\xc0\x69\xc4\x00\x02\xfd\xcc\xbc\x5c\x2f\x02\xc4\x8b\xfd\x5b\x2d\x5b\xeb\xda\x0d\xc1\x2b\x2f\xe4\x02\x35\xe8\x10\xcd\xaf\xb5\xfd\x56\x00\xc5\x4c\xe1\xff\x4c\xfe\x0f\x58\xbb\x3d\xdb\xda\x02\xa0\x25\xa7\x61\x27\x2b\xa6\x3b\xf3\x5e\x6b\xa8\xe0\x4e\x91\xd0\xff\x00\x20\x66\x88\x7e\xda\x25\x88\x00\xb9\x16\x19\x94\x88\x9c\xb9\x43\xd9\xff\x37\x2d\xae\x6f\x91\x80\x2e\x14\x30\x6a\xc5\x43\xdf\x87\x6c\x31\x04\x97\x48\x84\x8c\x70\xb5\x5a\x22\xc9\x06\x7e\xb6\x6d\x86\xb8\x1a\xed\x19\xe2\x01\x25\x1c\x19\xe8\xb6\xfa\xdd\x6e\x2b\xfd\x13\x48\x71\x17\x88\x08\xf3\x15\x00\x30\x08\x3c\xec\x28\xe4\x77\x3e\x73\x4a\xb2\x5f\x01\xe0\xce\x0c\xf9\x30\xff\x16\x80\xff\xcf\xd0\x64\x08\xb6\x9f\xed\x38\xd4\x0f\x28\x41\x44\xf0\x1d\xdd\x96\xef\xe4\xc8\xdf\x36\x3a\x67\xe8\xfa\x2d\x4f\x4b\x32\x77\x45\xc9\xab\x9a\xb8\x9d\x5b\x38\xb9\x85\xa3\xf4\xbd\x90\x9d\x76\xfe\x9d\x7d\x31\xc2\xee\xff\x46\xfc\x08\x20\x83\x3e\x12\xd1\x7a\xd7\x73\xab\x45\xad\xd0\x65\xcb\x8a\xf9\xf5\x0c\x01\xec\x02\xaa\x34\x66\xda\x09\xc8\x4e\x5b\xe5\xac\x93\x9f\x87\x80\x0b\x86\xc9\x34\x79\x8d\xc9\x10\x48\xd1\x4d\x5e\x30\x74\x17\x62\x86\xdc\x21\x10\x2c\x44\xf5\x65\x32\x5d\xa4\x00\x70\xe4\x84\x0c\x8b\x85\xd9\xf2\x25\x82\x0c\xb1\x21\xf8\x27\xf8\x57\x89\xdc\x26\xb0\x24\xa8\x97\x8b\xb3\x93\xbc\xe4\xbe\x41\x02\xc0\x1c\xbd\x72\x17\x49\xf8\x94\xe1\xd2\xd2\xd6\x8f\x24\xb5\x2d\xab\xd4\x66\x88\x6f\xe5\xba\xa2\xaf\xd0\x0f\x3c\x13\xd1\xf8\xc9\x74\x3b\xd5\xcd\x8a\xad\xec\x43\xc7\x50\x77\x6c\x40\x5a\x65\xcb\xe6\xba\x20\x72\xc0\x87\xc2\x99\xc9\xed\x42\x8a\xa3\x94\x1f\xa4\x34\x7f\xc4\xd2\x41\xb7\xf7\x38\x2c\x3d\x65\x8c\xb2\xfa\xac\x1c\x74\x7b\xab\x32\x30\xed\x5a\xca\xb6\xe3\x50\xcc\x80\xa0\xb7\x88\x48\x83\x00\x93\x39\xf4\x8c\xe5\xdd\x1a\x74\x07\x3f\x08\x93\x06\xab\x33\x69\xb0\x8c\x49\x17\x34\x95\xa5\x9c\x8c\xa1\xaf\x98\x0b\x9e\x32\x6c\xef\xb1\x16\x6a\x43\x86\xed\x75\xbb\xab\x32\x2c\xed\x5a\xca\xb0\x0f\x04\x7d\x0d\x90\x23\x90\x0b\x90\xc4\x0b\x50\x47\x59\x55\x6e\xe3\xfd\xaa\x89\xf9\xb1\x66\x55\xcf\xcb\x2c\x14\x08\x3c\xcc\x85\xdc\xe7\xb2\xc2\xc0\xab\xcc\x94\x65\x9d\x8a\xbb\xaf\x44\xd9\x36\x11\x69\xcb\x9d\x00\x4e\x8d\x49\x58\xda\x9c\xe3\x6f\x4d\x9a\x53\xe6\x22\xf6\x72\xd1\x64\x00\x04\x99\x33\x6b\x3d\xf9\x8d\xec\x1c\x73\x51\xae\x12\x97\xcc\xd4\x66\xef\xa8\xb7\x77\x6c\x54\xe1\x52\x55\x98\xb3\xeb\x1b\x5a\xf4\xb1\x72\x0c\xa4\xc7\xbb\x4c\x3b\xde\x43\x31\x3a\x0c\x41\x81\x4c\x2c\x81\xa9\x16\x5f\xa9\xcf\x2a\x38\xf2\x25\x5d\x32\x36\x5d\x58\xd9\xd2\xae\x00\xa5\x1f\x70\x17\x22\xb6\x30\xf8\xab\x9d\x12\xc8\x17\xc4\x29\xe3\xfa\x7b\xc4\x26\x94\xf9\xca\xf2\x83\x2a\xfa\x00\x30\x01\x90\xe8\x5e\x33\x46\x09\x0d\x39\xf0\x21\x21\x88\x6d\x55\x4b\x9b\x76\x4f\xc6\x94\x7a\x08\x12\xe3\x8b\xc5\x21\x01\xb1\x95\xf9\x92\xba\x06\x83\x4b\xc2\x32\x86\xa3\x6a\x5d\x1c\xd5\x4b\xc3\xbe\x30\x6a\x69\xc0\x4b\x8d\x64\x76\x85\x94\xad\x8f\xa4\x97\x9e\xbc\xd2\x95\x52\xcf\x92\xcf\x00\x69\x55\x39\x77\x65\xdb\x47\xff\x91\xb7\x8f\x72\x6d\xe8\x38\x28\x10\x28\x63\x3c\xff\x18\x0a\x70\xd0\xed\xaa\x79\xc1\x94\xac\xbe\x5b\xe4\x41\x94\xf2\xe9\x37\xb9\x4b\xa8\x96\x5a\x21\xf2\x54\x23\x1a\x9c\xdb\xec\xaf\x1b\xdf\xac\x96\x6f\x76\x9d\xfa\xf6\xc8\x95\x3a\x83\x86\xcc\x41\xc0\xa5\x88\x93\x6d\xa1\xfd\xb3\x8d\x4d\x92\x13\x2c\x02\xc2\x32\xb3\x44\xef\xf6\x71\xd4\x24\xbb\x49\xd7\xf1\xc2\xee\x61\x67\x48\xb3\xbb\x08\xe7\x0f\xe6\x7d\x3d\x69\xdf\xa8\xa9\x5f\xb4\x71\x89\x36\x2e\xd1\xe3\x44\x87\xf8\xce\xbf\xab\x33\x17\x4b\x16\x23\x76\x5b\x0f\xa1\xd2\xcc\x98\x52\x5e\xa1\xe5\x12\x01\x36\xf5\x65\x6f\xf2\x34\x75\x47\xcd\xc8\xfc\x26\x28\xbf\x31\xfc\xea\x30\x69\x13\x94\x6f\xc4\xb0\x7b\xa9\x5d\xdd\xd4\x43\x02\x7d\x4f\x5d\xa8\x47\x28\x55\x87\x27\xea\xf3\x32\x8d\x58\xda\xca\xae\x14\x9f\xca\x42\xb1\xd0\xb0\x71\x77\xff\xb0\x5a\x4f\x4f\xf0\x3d\x74\x5f\x06\x40\x95\x06\x54\x56\x51\xbc\x8d\x82\x2f\x58\xcc\x00\x0f\x90\x83\x27\x18\xb9\xe0\xec\xe4\x47\xd6\x84\xf7\x63\x62\x1e\xc0\x8a\x5a\x31\x90\x3b\xcc\xf7\x54\x8a\x6a\x80\x52\x9d\xf8\x5e\x7e\x5d\xa6\x12\xcb\x1a\x2d\x8f\x45\x9f\x40\x01\x81\xa0\x1a\x89\x5c\xd1\x8e\x94\xa5\xad\x0a\x31\x31\x85\xc4\x47\x6c\x8a\xda\x0a\xca\x7f\xd7\x8d\x54\xeb\xb0\x3a\x1d\x7f\x46\x8e\x28\x01\x2b\x41\x35\x84\x9a\x73\x58\x7f\xbe\x7a\x77\xa1\xf9\xf3\x1c\x5c\xbe\x7e\x05\xf6\x8f\xba\x7d\xd0\x4e\xea\x0e\x05\xa5\x1e\xef\x60\x24\x26\x1d\xca\xa6\x3b\x33\xe1\x7b\x3b\x6c\xe2\xc8\x56\xab\x61\xbb\xfe\x10\x7d\xd2\xf9\x8f\x10\x22\xdf\x78\x02\x7f\xde\x3d\x71\xe3\x09\xfc\x08\x9e\x80\xa5\xd6\x34\x2a\x47\x6e\x5a\x6d\xea\x44\x55\xcc\x4d\x72\xd4\xd9\xd2\xe7\xea\x2c\x74\x8a\x16\xa8\xbd\xf3\x2e\x49\x59\x03\x27\x03\xb3\x46\xea\x3a\xd7\xe3\x4f\x97\xc2\x8e\xc8\x7f\xbc\x54\x76\x24\x05\x2b\x66\xb4\x75\xe7\xf5\x24\xb6\x2d\xb0\x7e\xc8\xfc\x76\x44\xc8\x26\xcd\xbd\x49\x73\x1b\x9c\xdb\xd8\x38\x35\x98\xb4\x49\x73\x3f\x2d\x33\x67\x85\x34\x77\x66\x43\xaf\x55\x74\x9c\x33\x59\xee\x9b\xf6\xce\x83\xab\x93\xfd\x76\xb2\x7d\x6a\x27\xc0\x73\xfd\x1e\xba\x02\xf9\x69\xa6\xb1\xa2\x09\x68\x5c\x21\x9c\x63\xe6\x46\xbb\x6f\x32\xe2\x0f\x7c\x5e\x22\x96\x40\xf3\x88\x5f\xf4\xae\xe1\x29\xbf\xb4\x97\xdd\x01\x28\x3b\xe8\x97\xf5\x86\x1e\xfe\xac\xdf\xfd\x75\xb1\x99\xaf\xcf\x79\x98\x65\xa7\xfd\x2a\x9c\xc6\xea\xa6\x4f\x5a\xff\xd5\x8c\xe1\xc5\x0e\xe0\x26\x96\xb7\xb1\x73\xeb\x30\x69\xc5\x58\x5e\x2c\x66\x9b\x98\xde\xaa\x79\xac\xf0\x41\xd4\x67\x18\xb8\x96\x18\xdd\xcb\xc5\x99\x9b\xd7\xa2\xa1\x1b\xc0\x6c\x1e\xbf\x4a\x91\x2e\x6d\x5d\x3f\xd7\xa5\x51\x74\x57\xcc\x74\x3d\x48\xf0\xaa\x41\xb4\x28\xab\x32\xb2\x51\xba\x68\xcd\x70\x01\x45\xa8\xee\x47\x89\x48\xdf\xe8\xe5\x8d\x5e\x5e\xb3\x5e\xde\xa8\x64\x1b\xcf\xd6\x52\x70\xb5\x06\xad\x9c\x2b\xbc\x2a\xb1\x6b\x8b\x95\x55\x55\x1a\x79\x69\xeb\x4d\x3d\xd6\x46\x2f\x3e\x11\x26\x3d\x60\x3d\x56\x12\x98\xdd\x94\x62\xad\xb3\x14\x6b\x7d\x51\x90\x1d\xe8\xba\x94\x8c\xd2\x28\xc8\x26\x2c\xb2\x5a\x58\xe4\x58\xf2\xf1\x7d\xc2\xb5\xfc\x6e\x52\x12\xfa\xd8\xe6\x40\x4d\x80\xc1\x6f\xdb\xee\xd2\xb8\xf7\x93\x8a\xa5\x64\x59\x53\x19\x49\x96\x22\x93\x12\x03\xc4\x0c\x0a\xc0\x67\x34\xf4\x5c\x30\x46\x20\xe4\xfa\xb6\x41\x87\x92\x09\x9e\x86\x0c\x29\xc1\xd2\xf7\xf4\x99\x1e\x8c\x66\x0a\x25\x5a\xee\x34\xaf\x3a\x9b\xed\xec\x8f\xba\x9d\x6d\xc2\x2f\x3f\x92\xad\x6f\x29\xa9\xba\x80\x3e\xe2\x01\x74\x9a\xdf\xe1\x47\x92\x9e\x8d\xea\xaa\x32\x03\x82\x25\x95\x55\x49\xe3\x46\xfb\xc5\xb2\xda\x2a\x92\x83\x5a\xa7\xba\x2a\xdf\xa7\x49\x69\x52\xd2\xf7\xf1\x8a\x93\x12\x46\xae\x56\x9e\x94\x74\x5f\x4b\x81\x92\x1d\xda\x0f\x59\xa2\x94\x90\xb2\x29\x52\xda\x14\x29\x19\x9c\xdb\x58\x0f\x35\x98\xb4\x29\x52\x7a\x5a\x86\xc3\x2a\x45\x4a\xd9\x7d\xb1\x96\x0f\x58\xb0\x00\xee\x5b\xa8\x54\x04\x58\xa7\x54\x89\xe4\x7b\xd5\x2e\x56\x2a\xf4\xdc\x5c\x98\x98\x3e\x0f\xb3\xdd\x36\xae\x89\x2a\xcc\xd9\x66\x3b\xd9\x54\x45\x3d\x70\x55\x54\x2a\x83\x66\x44\x30\x79\xdb\xb0\x32\xca\xec\x67\xf7\x40\xca\x82\x80\x79\x5f\xe6\xe1\xc3\x80\xeb\xd8\x02\xcc\x40\x60\xc1\x4f\x2c\x8b\xfd\x55\xba\x7e\xcb\x1a\x3f\x71\x9d\x58\xb3\x4e\x2a\xf5\x46\x37\x95\x52\x1b\x63\xbb\x0e\x93\x56\x0c\xd5\xa5\x82\xb6\x09\xd6\x7d\xcf\x5a\xa9\x75\x28\xd3\x5c\xb5\x54\x02\xb2\x66\xbd\x54\xa5\x5a\xad\xd1\xfe\x87\xac\x99\x2a\x8f\xa9\xad\xa7\x6a\x2a\x81\xbf\xa9\x9b\xda\x68\xe9\x07\xd0\xd2\x1b\x05\x6d\xe3\xda\x7a\x2a\xa7\xd6\xa1\xa3\x73\xb5\x53\xa5\x36\xaf\xa5\x1e\xaa\x52\x3f\xd7\x68\xbf\xa9\xa0\xda\x68\xc8\x27\xc2\xa4\x4d\x05\xd5\x9f\xa8\x82\xca\x88\x98\xa0\x39\xf4\xd6\x9e\x68\x3e\x9d\x43\x2f\x54\xef\xd6\x99\x69\xe6\x33\xca\x04\xf0\xf0\x5c\xd2\x9e\x8c\xb0\x52\x02\xba\x56\xf7\x1f\x35\x17\x2d\xb9\x7f\xcf\x7c\xb4\x04\xb1\xde\x9c\x74\x01\xe2\x26\x2f\xfd\xbd\xb1\xde\xe4\xa5\x1f\x8c\x73\x1b\x13\xa3\x06\x93\x36\x79\xe9\xa7\xe5\x82\xdd\x2f\x2f\xbd\x95\x8e\x2a\x91\x8b\x28\x1c\xea\xeb\x0c\x9f\xe9\xff\x07\xaf\xa8\xef\x53\x12\xbd\x52\xff\x39\xc7\xa9\x91\x91\x28\x7e\xc3\x18\xb8\xc5\xc4\x35\xfe\x0c\xe0\x14\x19\x7f\x72\xfc\xcd\xfc\x53\x50\x01\x3d\xe3\x6f\x2c\x90\x1f\x9b\x25\x96\xfb\x1c\x03\x26\x6d\x15\x81\x4d\x56\xcb\xf1\x96\x26\x68\x24\x16\xc5\x46\x98\x08\x34\x35\x8b\xbe\xf1\xb7\x1a\xad\x14\xce\xe5\xcd\xd4\x07\x25\x26\x71\x1b\xe8\x79\xef\x26\x26\x8b\xaa\x04\xec\x9d\xa2\xf7\x12\x4d\x10\x43\xc4\xc9\x64\xb6\x4b\x2e\xb8\xb4\x31\x05\xa8\x35\xe1\x16\xa4\xce\xca\x1c\xa0\x66\x12\x5a\x56\x48\x69\xf3\xc4\x64\x1c\x61\xb7\xb2\x93\xfa\x96\xa3\x69\xd8\x6c\x82\xf1\xf2\xe9\xad\x25\x03\x33\xc9\xf5\xb2\x46\x06\x9e\x6f\x91\x80\x0d\x51\xa4\x5f\x08\x62\x4b\x11\xd0\xa6\xb5\x3b\x82\x19\x3d\x35\xa1\xcc\x87\x62\x28\xcd\x4e\xd4\x16\xd8\x47\xcb\xc0\xf8\xd4\x55\xee\xd6\xaa\x70\xd4\xfb\xe8\x67\xc0\x23\xbb\x08\x53\x72\x85\x84\xd4\x16\xbc\x6a\x69\x63\x73\x61\x87\xcc\xbb\xdf\xa4\x85\xcc\xb2\x8a\x2c\x38\x1e\x3b\x0e\x0d\x49\xa5\xce\x71\x3c\x8c\x88\x18\x65\xf0\x8b\xde\x71\xe4\x30\x54\x35\x77\x49\xdf\xe5\xf3\x67\x42\xac\x46\xfd\x04\x05\x1e\x5d\xf8\x88\x88\x73\xaa\x77\xa0\x86\x12\x65\x26\xb1\xab\x87\xca\xfd\x66\xf9\x03\x29\x1d\x64\xdb\xd5\xd4\x32\x04\xad\xe3\xf7\x67\x11\x52\xd9\x8d\x12\xcb\x8f\xf3\x5e\xf6\xe5\x4c\xa3\x55\xf2\xc3\xf6\x39\x85\xe6\x79\x5a\x58\x0b\x3b\x6d\x5b\x03\x57\x6e\x32\xcf\x6f\xcf\x4b\x06\x29\xfe\x64\x63\xa1\x7f\x44\x58\xe9\x6f\xf0\x94\xab\xe0\x52\x8c\x35\x5f\x21\x63\x70\x91\xfb\xa2\xf6\xc0\xa2\xb9\x90\x9b\x50\x93\xf6\x46\x53\x9b\xd9\xde\xa3\x25\xc6\xcd\x0d\xfe\x17\xc9\x8e\x72\xc5\x90\xb1\x40\x7e\xa2\x9e\xcb\x63\x0b\x43\x9d\x7f\xd1\x1e\x81\x3e\x10\x23\x21\xc8\x7f\x42\x0d\x13\x9c\x11\x2e\x20\x71\x50\x67\x15\x19\x2d\xd5\x58\xe9\x44\x3c\x8b\xee\x5a\x8f\x22\x52\x8e\x31\x2f\x69\x9b\x12\x91\x7e\x96\x9d\x45\xad\x80\xd4\xd0\x97\x68\x8a\xb9\x60\x8b\x35\xb3\x44\x01\x07\x31\xf0\x07\xe0\x8d\x6e\x0c\x58\x3c\xe2\xba\xb8\x14\xcb\x92\x3a\x52\x95\x91\xa4\xec\x21\x2b\x2b\xb7\x5a\xc7\xf9\xe3\x62\xad\xb5\x5b\x07\x73\xe8\x85\x16\xbb\xce\x54\xa2\xc5\xe3\x60\x65\xd8\xc6\x15\x74\xf9\x43\x6e\x59\xb4\xcd\x75\x9d\x5b\xcf\xf5\x4f\xa5\xb5\xf2\xa6\x78\xf1\xba\xdf\x84\xd5\xf9\xa3\x7f\x57\x02\x8a\x9c\xa1\x95\xe1\x0a\x22\xa1\x6f\x4a\x97\x8b\x79\x24\x9d\xc8\xdc\x44\x19\x82\xee\xc2\x3e\x42\x14\xa0\x32\xad\xa5\xb2\x8d\xac\x9a\xf7\x25\x80\xed\x13\xa0\x97\xa4\x34\x76\xcc\xe2\x9c\x34\x01\x0e\xa0\x8a\xdf\x81\xc0\x83\x04\x19\x67\x12\x75\xa6\xb8\xb5\xca\xe2\xaa\x20\xbc\x65\xa7\xc0\xe4\xc9\x0a\xfb\xb0\x86\xfc\xbd\x90\xbb\x52\x9c\xa8\x9a\x32\x9e\x69\x51\xb2\x16\xcb\x3a\xc7\x00\x0a\xae\x47\x13\x2a\x94\xf4\xe6\x42\x9f\xa6\x47\x55\x5f\x98\xd6\x6d\x0e\x35\xa1\xe2\x3e\xf3\x78\x15\xc9\xab\x95\x28\x53\x3f\x35\x22\x2c\x6b\xb7\x34\xf6\x28\xad\x96\x49\x63\x43\xa6\xd9\x15\x67\x76\x15\x68\xbc\x7d\x35\x83\x84\x20\xaf\x42\xd7\xb9\x68\x02\x43\x4f\xc8\xb7\x70\xec\xa1\x12\x0d\x18\x7d\xcc\x32\xfc\x04\x71\xe9\x6b\x34\xd5\xa6\x5a\x6d\x9a\xb0\x69\x10\x64\x14\xab\x1b\x65\x63\xb3\xc3\x35\x1d\x07\x72\x8e\xa7\xc4\xdc\xeb\xe2\x77\x99\xc1\x94\x6a\xcc\xb6\x5a\x8e\xe1\x04\x62\xaf\x88\x72\x16\x8a\x9b\xcb\x29\xb7\xa5\xe8\xcc\xb1\x34\xfd\xf3\x0d\x33\x1f\x72\x52\x6d\xda\x49\x95\xa1\x25\x69\xdd\x99\x48\x6b\xb3\x67\x04\xb5\x87\x68\x7c\x29\xfc\x70\xb9\x2d\x70\x24\xa1\x99\xe2\x59\x25\x98\x25\x46\x71\xba\x98\x72\xb8\x14\xe1\x6e\x57\x59\x6e\x91\x8f\xbb\x9d\x82\x53\xdf\x47\xb1\xb1\x56\x17\xcd\x65\x16\x6b\x8a\x6f\xc2\x21\x13\xf4\x33\x23\x50\x58\x65\x1e\xca\x96\x6a\x9b\xe5\x33\x18\xa0\xcc\xeb\x80\x51\x07\x71\x6e\xfe\xee\xa8\x7c\xad\x83\x93\x33\x48\x5c\x2f\x1b\x4b\xca\xa8\xa0\xac\x5c\x58\x2c\x0c\x9b\x54\x48\x0b\xc3\x36\xf5\x23\x09\x3a\x1b\x13\x70\x13\xb7\x7c\xe4\x45\x7e\x79\xe6\xab\x5a\xec\x23\xb5\x7d\xad\x6a\xd2\x14\xf8\x1b\xa3\xb1\xbc\x47\x56\x91\x2d\x9b\xea\x48\xef\xa5\x33\x6a\x21\xae\x2e\xac\x62\xb8\xc2\x04\x6b\x70\xa5\x36\x72\x36\x05\x9a\xdf\xcd\x72\x86\xde\x6a\x46\x59\xc6\xe0\x69\xd8\x37\xa3\x78\xf2\xd8\x3d\x86\x11\x57\x42\x4c\xc3\x6d\x3a\x4e\x9e\x8c\xe6\x3a\x0a\x63\xdf\xb1\xf3\x61\x6d\xfd\xc4\x51\x44\x4c\xc4\xfe\xc0\xb2\x3b\x3d\x59\xcb\x71\x0d\x26\xe3\xa3\xd8\x8a\xeb\x10\xdc\x86\xbd\xed\xb6\xe5\x9f\xc0\xa8\x6c\xd9\x8d\x49\x70\x9d\xfc\xca\x78\xde\x9b\x96\x5f\xac\x8e\xe8\xdf\xdb\x09\x0a\x97\x28\x60\x88\xcb\x11\x33\xa5\x82\xaa\xb6\x9f\x87\x41\x40\x99\x40\x2e\x18\x2f\x94\xc3\x7a\xfc\xfe\x2c\xea\x48\x09\xca\xf2\xb8\xb8\xb7\x81\xe2\xfe\xa6\x5f\x45\x0b\x3b\xf7\x56\xd3\xbb\x4e\x88\x9f\x39\x25\xa3\x0c\xd8\x47\xca\x65\xe5\xb7\xdc\xc2\x7c\x5c\x40\x1f\x15\x0f\x74\xc9\x51\x3a\x55\x0a\xc0\xfc\x50\xa2\x2d\xb3\x45\x0f\xba\xcd\x3d\x47\x8a\x76\xfa\x82\x18\x67\x4b\x93\xa2\x46\x4d\xc6\x5a\xd7\x82\xc9\x9b\x16\x79\xe4\xaa\xf0\x3e\x36\xff\x2c\x20\x5f\x9b\x47\xd8\xa1\x64\x94\x4f\xd9\x15\x06\xfb\x70\x79\xae\xa2\xa9\x44\xb5\x5f\x7d\x34\x0f\x8e\x97\xcd\xc7\xb9\x6a\x92\xde\xf7\x04\x05\x9a\x52\x86\xbf\xa1\xec\x90\xf7\x9d\x97\x72\xa1\x81\x01\x1c\x63\x0f\x17\x17\x87\xed\x54\x9b\xd1\xb8\xa8\x84\x1c\x39\xdf\xdf\x15\x59\x7b\x69\x45\x99\x06\x8d\x9f\x63\xa5\x70\xe2\x40\xb5\xba\x68\xcb\x81\xc4\xbc\x65\x6b\xae\x8b\x8e\x10\x80\x05\x33\xb2\x00\x2d\x5d\x30\x13\x8c\x3c\xd7\x2e\x0b\x05\x0d\x04\x4c\xa5\xf7\xe3\x10\x50\xdc\xb6\xfe\x04\xfb\xb9\x24\xb3\x34\x48\x9e\x2b\x73\x7d\x96\xe5\x50\xfe\x8c\x52\x03\x27\xb3\x41\x06\x77\xa9\xcf\x07\x09\xa1\x02\x16\x12\x84\x76\x76\x59\x58\x55\x2a\xc4\x65\xb3\x63\xdf\x4a\x2b\x56\xb2\x25\x7d\xb2\xa4\x87\xdd\xea\xb0\xda\x1d\xca\xf2\x90\xe0\xb7\x4a\x66\xe7\x31\x9c\x30\x9b\x68\xe4\x8d\xe5\xa4\xcd\x35\x22\x30\x8d\xf6\x58\xa6\x63\x49\x89\x94\x8b\xa5\x46\xf1\x31\x81\x99\x50\x4c\x34\x7d\x8b\x0b\x75\xb6\x3d\x53\x52\xe5\xc3\x20\xc0\x64\x6a\x72\x37\xe4\x88\xd5\x27\x4b\xa3\xfc\x81\x67\x7f\x40\x82\xb2\x29\x24\x98\xc3\x28\xcf\xd2\x08\xd6\x3b\xa3\x6f\xab\xcc\xbc\x6d\xc6\x7b\x1b\x92\xab\xc1\xc8\x22\x57\x39\x8d\x72\xb8\x86\x75\x1a\xb5\x2a\x8f\xe4\xfc\x2c\xad\xe3\xa8\x83\xff\xf7\x40\xce\x9c\xf8\x15\x90\xac\xca\xd6\x1d\x5b\x8f\xd3\xe9\x32\xa8\xdc\xb9\xfa\xd5\x82\x3f\xd5\x15\xf3\xcd\x2b\xe6\x92\xcb\x63\x2b\xa3\x06\xe9\xeb\x4a\x07\xab\x84\x61\x06\xaa\x76\xa6\x91\x25\x87\x1b\xbe\x0b\x03\xeb\xe8\xba\x3f\x81\x45\x61\x1c\x0f\x28\x61\x42\x7d\x41\x5f\xb3\xc7\xdc\x0c\xff\x7b\x86\x10\x6d\x46\x42\x53\xc7\xb9\x7e\x98\xd1\xfc\xd2\x78\x0d\x02\x80\xbe\x06\x98\x15\xe2\xdd\xf2\x79\x56\x55\x14\x59\x09\x53\x64\xf6\xf5\xf8\x59\x65\xff\x69\xd5\xd0\x17\x46\x92\x02\x34\x8b\xd0\x58\xee\xbb\xd6\x1f\x34\x01\x52\x8a\xf3\x77\xe6\xa4\x9c\xd5\x37\xe7\x24\x5d\x0b\x5e\xe4\xd9\x89\xf4\xa8\x19\x72\x28\x4b\x2e\x3b\xc8\xf9\x45\x16\x06\xe6\x2e\xc3\xb1\x1c\x0e\x32\xab\xb1\x35\x0e\x46\x95\x78\xfe\xa7\x98\xb3\x3f\xb8\x0c\xa7\x08\x60\xe2\xa2\xaf\x05\xe8\x13\xe8\x71\x54\x1f\xcb\x62\xcd\x7e\xbe\x46\x5c\xdb\xbd\xa0\x15\x55\x21\x9a\xc5\xe1\x1a\x69\xa3\x96\xbd\x12\xe9\x8b\xd0\x1f\x23\x26\x59\xa9\x54\x13\xc0\x04\x20\xe8\xcc\x4c\xa2\xd7\x48\x46\xbe\x88\x3d\x21\xa3\xdb\xd5\x84\x44\x37\xa0\x59\x15\xd9\x7f\x52\x9f\xf6\x2a\x3a\xd6\xa9\x6b\xd9\x54\x27\x30\x5e\x00\x87\x61\x81\x18\x86\x1d\x25\x21\x7c\x41\x04\xfc\xaa\xe3\x2e\x98\xa7\xa2\x06\x30\x37\x10\xf2\xb1\x07\x99\xf4\x7e\x45\xae\x0b\x02\x37\x31\xe0\x1b\xe0\x78\x30\xe4\x2a\x88\x07\x09\xb8\xfa\xf5\x5c\x27\x03\x7c\x44\x44\xea\xf9\x9e\x4a\xbe\x29\x46\xc7\x8e\xb5\xea\xaf\x43\x1b\x90\x2c\x12\xb0\x19\x1f\xf1\x46\x3b\xd0\x3c\x85\xf3\x9a\xb2\x98\x75\xcf\x25\x62\x4c\xdd\x6a\x27\x75\xb5\xe1\x41\x4a\x76\x73\x73\x00\x31\x43\x58\x2b\xf8\xe7\xd2\xa6\x53\x23\x4d\xa8\xe7\xd1\x2f\x98\x4c\x23\xc2\xa2\xa2\x38\xf9\xdc\xdc\xdc\xf0\x3b\x2f\xe3\x10\x02\xc8\x1d\xf3\x7b\xda\xf8\xba\x39\x12\x60\x04\x89\x3b\x8a\x15\xc3\x7d\x50\x7a\x1e\x03\x29\xc7\xef\x4c\x33\xd6\x9c\x61\xb2\x2d\x74\xbe\xdf\x45\xee\x73\x40\x19\xc0\xba\x8d\x92\x38\x80\x39\x40\x7e\x20\x16\xcf\xe5\xbb\x54\x6d\xe9\xaa\x2d\x1e\x7a\x82\x03\xc8\x32\xf3\x27\xb1\xe9\x24\x72\x1d\x78\xd4\x45\x99\x13\x85\x45\x59\xcf\x89\xb2\x29\xee\x31\x69\xad\x92\x15\xaa\x97\x70\x04\xe0\xbe\xab\x90\x8b\x85\x87\x86\x6a\x57\xd3\xba\x42\x5d\x19\x68\x5f\x61\xe9\x02\x53\x8d\xd2\x05\x65\xc8\x42\xf5\xca\x5a\xb2\xa2\xbe\xcc\x10\x43\x99\xe5\x94\x0e\x99\x59\x55\xe0\x58\xca\x09\x72\xa3\xd5\x21\xf5\x92\x02\xa7\xf1\x92\x93\x73\x23\xb9\x74\xf3\x1c\xdc\x18\x24\xc8\x3f\x23\x69\x91\xff\x54\x91\xd3\x9b\xe7\x00\x12\x17\xdc\x44\x81\xed\x9b\x74\xa1\xc5\x43\xe8\x03\x23\x94\xe9\x49\xbf\xf9\x9f\xbf\xcb\xbe\x2f\x6e\x94\xd8\xdc\x9c\x9f\xfd\x72\x6a\xe9\xe3\x50\xf2\x39\x24\x8e\xc0\x73\x94\xef\x7f\x7c\x71\x72\xa3\x87\x7c\x77\x79\xd3\x01\x3f\xd1\x2f\x68\x8e\xd8\x73\xb0\xa0\xa1\x52\x0c\x92\x72\x08\x7c\xf8\x15\xfb\xa1\x2f\x79\xd0\xeb\xa6\xe0\x28\x51\xb4\xc2\x98\x52\x25\x16\x06\xfb\x4f\x13\x39\xb3\xad\xce\x5c\xde\x48\x9f\x81\x97\x7c\x53\x12\x77\x03\xbf\xf0\x36\xbf\xe3\x6d\x9d\x82\xd5\x48\xaa\x98\xab\x66\x0d\xb8\xd1\x75\x46\x37\x75\x97\x6b\x76\xad\xbe\x00\x59\xf8\x0a\x7c\x0c\xfa\x45\xb6\xc0\x49\x75\xff\x67\xd0\xfe\x97\x9d\x0c\x5d\x92\x8d\xa3\xb2\x63\x4d\x06\xd4\xa3\xe8\x9f\x38\x10\x90\x09\xae\xdf\x4b\xaa\x56\xc4\xd8\xc3\xb7\x48\x22\xfd\x97\xfe\xde\x77\x51\x2c\x4a\x5d\xca\x8f\xd9\x69\x31\xf4\x0d\x14\xea\xbb\x74\xc2\xc1\x0c\x72\x10\x20\xe6\x63\xce\xa3\x9a\x6c\x8e\x90\x12\x29\xcd\x17\xe4\x1a\x72\x70\x41\x05\xea\xc4\xf8\xe9\x4d\x27\x3d\xbf\x29\x25\x3e\xaa\x6a\xc1\xdc\xe8\x5d\xae\xbe\x22\xa3\x41\xc9\x5c\x89\x52\xb2\x2b\x20\xcb\x1e\x9f\xd1\x2f\x20\xaf\xf6\x6a\x49\x49\x6b\x35\xf5\xb6\x95\x5e\x01\xa0\x8a\x8d\x62\xb4\xa2\x3b\x00\x4c\xa0\x68\x08\xc6\xea\x6d\xf4\x52\xff\xf1\x3a\x32\xc9\x7f\xfe\x78\xbd\x65\x8e\x38\x13\x22\x90\xd0\xb3\xd4\xe6\x0b\x02\xad\x67\xda\x73\x11\x4a\xcd\xe8\xd6\xdb\x45\xe6\x77\x52\x33\x16\x41\x35\x00\xec\x0e\x81\x47\xa7\x23\x8e\xc9\xed\xa8\xdb\xe9\x25\x1f\xf4\x39\x90\x0c\xa4\xe4\x5b\xa3\x33\x26\xaa\x28\x88\xef\x98\x83\xb4\x72\xf8\x9f\xd3\x29\xb8\xc2\xe4\x36\x79\x1d\xbb\x59\xa0\x95\x69\x6d\x4b\x26\xb6\xf3\x9a\x20\x9b\xc9\xca\x43\x4e\x73\x6d\x2b\xe2\xdf\x09\xc8\x34\xc5\xa8\x98\x4c\x6b\x03\x6e\x8e\x57\x96\xca\x6a\xab\xa2\xb2\x51\xbe\xa8\xac\x6d\x2b\x2a\x2b\x26\x68\xca\x0f\xe1\xf8\x7e\xd1\x35\x4c\x97\x5a\x7a\x6b\x45\xfc\x08\x2c\x3c\x3d\x03\x56\x77\xd1\x12\x6e\xaf\x0a\xb8\x03\xe0\x87\x9e\xc0\x23\x0f\x13\xeb\x29\xe0\xa4\x3c\xd5\x5c\xf3\xd9\x06\xc6\xe4\xbd\x95\xb0\xc0\x39\x26\xb6\x96\x11\xe2\xd5\x6d\x14\x0d\x63\x4a\x3d\x04\x89\xe5\xfb\xd7\xf6\x94\xd1\x30\x18\x82\x16\x22\x6e\x40\x31\x11\xc5\xf3\x50\x7c\x46\xbf\x8c\xa0\xe7\xdd\x9f\x9c\xab\x19\xfd\x22\x37\xfc\x72\x62\xaa\x5a\xdc\x93\x14\x41\x03\xec\x2c\xc9\xc2\x53\xdf\x97\x86\x82\xdc\x9e\x04\x72\x93\xe3\x1f\x7a\xf7\x54\x00\x74\xc4\xc7\x2e\x42\xd7\xe5\x0d\xca\x13\x2e\x29\xda\x6a\xd5\x65\x71\xe6\x02\x05\xf7\x0f\x86\xe5\xa2\xf3\xe9\xd3\xae\x14\xe4\x08\x26\xe1\x88\x89\x91\xb2\x1a\xcb\xda\x94\xfb\x95\xc5\xe7\xd8\x75\x55\xe9\x4c\xc8\x05\xf5\xb5\x31\x1a\x9b\x23\x0e\x55\xf6\x89\x88\xb6\xfe\xc8\xe0\xf5\x11\xe7\x3a\x10\x00\x04\x83\x84\x63\x91\xcf\x8d\xa6\xcf\x72\x72\xe4\xb3\x84\x96\x02\x3d\xd7\xb1\xbd\x17\x19\xdd\x1a\x69\x41\xa5\x47\x0a\x5d\x17\xb9\x95\xa0\x22\xe1\x78\x2d\x3b\x55\x37\x2c\x17\x12\xf3\x29\xc9\xd0\x55\x62\xaf\x19\x6a\xa2\x5f\x07\xe5\xdf\x54\xb2\xee\xde\x28\x97\xa5\x08\xcd\xa7\xbd\x14\xab\x38\x77\xb8\x04\xe7\x33\x25\xae\x9a\xdb\xe0\xd8\x11\xf9\x20\x5a\x11\x7b\xab\x82\xaf\x87\x79\x3b\xb3\x3a\xac\x8d\x96\x8c\x51\x67\x05\xa2\xaf\x82\x41\xa7\xd9\x12\x3c\xd5\x7d\x00\x8c\x84\x75\xc2\xa8\xaf\x26\x7f\x4c\xdd\xbc\xd6\x48\x9f\x3f\xfe\xf2\x59\x87\x2c\x46\x18\xc5\x2c\x7e\x28\x51\xcb\x88\xc1\xf7\x92\xb5\x19\xe4\xa3\x19\x82\x2e\x62\xa3\x09\xf6\x04\x2a\x54\xd4\xa6\x4f\x66\x8e\x5f\xab\xc6\x60\x0c\xb9\x74\xff\x75\x68\x41\x17\x4a\x3a\x6a\xde\x29\x41\x40\xc3\xbd\xa7\xf0\xd9\x0b\x1a\x4a\xf1\x92\xb2\xa7\xc7\x8d\x9c\x5d\x1a\xe7\xdb\xaa\x15\x5b\x7c\xe2\x3d\xea\x7c\x51\xac\x65\xc8\x3e\x91\x4c\xfc\xa4\x87\x5a\xde\x7c\x7d\xb2\x6a\x29\xb3\x28\xa2\x05\x79\x8c\x5a\x34\x51\xdf\x5f\x5c\x0b\x92\x54\x4f\x64\x53\x17\xb0\xb6\xef\xf7\x76\x71\x4e\xa7\x66\xd6\x69\xc9\xd9\x88\xcc\x6d\x0b\xa0\x75\x34\xe6\xf3\x2e\x3f\x10\x04\x1d\x4c\xbb\xfd\xe9\x6c\x6f\x3a\x30\xdc\x9b\xc2\xb9\x21\xa3\xcf\xfe\x98\x4d\x58\xb7\xdb\x0f\x26\xe4\x76\xd6\x35\x2d\xb7\xf4\x9a\x09\xd0\xe2\x6c\xee\xb4\xa1\xe3\x88\x76\x6f\xbf\x8f\x26\x7d\xf7\xb0\xdd\xed\x77\x8f\xda\x83\x5e\xef\xa0\x7d\x38\xd8\xef\xb7\xdd\xc9\xfe\xae\xd3\xef\xf6\xf7\x9c\xfe\xbe\x05\x4a\x74\x05\x05\x68\x8d\x7b\x83\x81\x7b\x74\xd4\x6b\x77\x0f\xd1\xb8\x3d\x18\x1c\xf4\xdb\x87\xc8\xe9\xb5\xd1\xb8\xbb\x3b\x70\xf6\x8f\xfa\xbb\xbd\xb1\xd9\x3f\x64\xde\x10\xb4\x26\x94\xb6\x6d\xf8\x76\x6e\x21\xef\x40\xc7\x47\x1d\x87\xfa\xc3\xc1\x60\xb7\x95\x73\xb7\xac\xe7\x91\x0c\xf2\xbb\xb7\x87\x1e\x99\x76\x77\x7b\x1c\x1d\xdd\xd5\x20\x1f\x75\xfb\x7b\xfd\xfd\x3d\xd4\x86\x87\x87\xb0\x3d\x18\x4c\xc6\xed\xc3\xc1\x5e\xb7\x8d\xdc\x6e\xaf\x8b\xc6\xfb\x63\x67\xcf\xa9\x22\xdf\x75\xf6\xe0\x61\xff\xe8\xb0\x3d\x46\xee\x41\x7b\xd0\xef\xa3\xf6\xe1\xd1\xe0\xa0\x3d\xd9\x9f\xb8\x70\xff\xa8\x7f\xd4\x9f\x4c\x8a\xe4\x8f\x21\x8b\xc8\xef\xfb\x13\x07\x76\xbb\x7d\x71\x74\x77\xc0\xa7\x1d\xce\xca\xc8\x8f\xcf\xe6\xe4\xfd\xea\xe2\x29\x1f\xd0\xb2\x3b\xf5\xd6\xf3\x56\x36\xd7\x34\xf1\xad\xcc\xd8\x91\x7e\x52\x3f\x92\x17\xbe\x46\xbe\x8c\x9a\xdc\xe7\x63\x98\xbd\x07\x36\xf1\xaa\xb3\x43\xdd\xa2\x45\x7e\xb5\xc6\x09\xd3\xd6\xd5\xf5\xe5\xd9\xc5\x9b\xac\xef\x61\xb5\x33\x93\x1e\x3f\x5f\xbd\xbb\xc8\x5d\x8a\x11\x39\xed\x85\x74\x67\xa5\x03\x11\x85\x6f\xd4\x57\xa9\x36\x8b\xee\x67\x1c\xec\x52\x4d\x94\x49\x5a\x76\x5c\x29\x77\xce\x51\xc5\xeb\x46\xf1\x29\x34\x73\x68\x17\x41\x77\xe4\x21\x21\x10\x1b\xdd\x85\x28\x4f\xa6\xe2\xae\x14\x38\xef\x2e\x17\x4d\xaa\xfe\x05\xc0\xb2\xc8\x94\xe5\x1a\x42\x23\xf1\xbd\x4c\x03\x95\x54\x0d\xc6\x3f\x85\xd2\xca\x86\x6f\x3a\x30\xc0\x1d\x1a\x20\xc2\x67\x78\x22\xa4\x6c\xef\x04\x8c\x4e\xb0\x87\x6c\xb3\x0b\x5a\x91\x03\xdf\xce\x34\x6a\x70\xc3\x64\x19\xcd\xb2\x83\x85\xee\x47\x20\xa6\xfc\xa2\x44\x4b\x20\xb0\xd5\xeb\x1a\x8a\x20\xba\x09\x26\x77\x0d\x5c\x75\xec\x4c\x5f\x8f\xb8\x93\x81\xa3\x2e\xe7\x02\xad\x57\xef\x2e\x2e\x4e\x5f\x5d\xbf\xbb\x6c\xbf\x7d\xf3\xf6\xba\x9d\x69\x12\x5d\xc9\x05\x5a\x57\x0b\xe2\xcc\x18\x25\x34\xe4\x00\xaa\x5d\x1a\x60\x0e\x08\x15\x69\x65\xb7\x8e\xcd\x43\xbe\x20\xce\x0b\xa9\x18\x8a\xd7\x69\xe4\xee\xec\x02\xad\x1e\xfe\x78\x86\xfd\xbb\x37\x0e\x3b\x09\xcf\xf7\x7b\xf0\xc3\xd7\xb3\x7f\xdc\xbd\xbc\xbe\xbb\xb8\x84\x09\x97\xce\x74\xac\xfb\xd7\x10\xb1\x45\x0d\x4e\xf5\xd7\xc4\xa9\xfe\x52\x46\xf5\x2d\x7c\xfa\x8f\x21\x00\xaf\xd5\xe1\x65\x69\xdb\x05\x90\x71\x94\xc9\xf4\x0c\xc1\x07\x22\x95\xb8\xfc\xaa\xc2\x39\x3a\x96\x13\x95\x53\x71\x75\xb7\x04\x0c\xf0\x48\x87\x3c\xa3\x73\xbd\x43\x50\xc0\x60\xd8\x60\xbc\xb4\x04\xdf\xa1\x5e\xe8\x13\x6d\x7a\xca\x91\xa2\x50\x3e\xd8\xc6\xee\x76\x07\x5c\xd9\xda\xa9\x9c\x97\x39\x9a\xdc\x85\x29\x79\x1e\x65\xa2\x1d\x8f\x86\xee\x28\xca\x97\xb0\xf8\xad\x3e\x8a\xd7\x01\xbf\xea\xbc\x85\x9e\xc8\x21\xc0\x2e\x78\x01\x7a\xfd\xdd\x52\xa9\xf0\x3e\x9e\xbc\x09\x17\xe3\x33\x76\x4a\xbe\xb2\x63\xe4\x1f\xf4\x07\xd3\xbb\xdb\x5b\x7c\x32\x8f\xa5\x22\x7f\x0b\xa4\x4d\x12\x06\xdd\xc1\x5a\x24\xe1\x60\x99\x20\x1c\x58\xd6\x4b\x9d\xab\x24\x13\x62\xac\xb7\x3e\xdb\x48\x3a\x78\x3c\x82\xd2\xd4\x96\x0a\x8b\x61\xf7\xc5\x76\x0f\xff\xb2\xeb\x86\xbf\x7d\x3a\x9b\xcf\xf7\x3e\xcd\xcf\xbd\xc5\xb7\x9e\xff\xe6\x72\xf7\xe7\xc5\xdd\xc5\xb6\x52\x0d\x13\x1a\x12\xb7\x62\xf1\x7f\x7a\x77\x30\xed\x4f\xf7\x7f\xba\x76\x3f\xfc\xf2\x01\xf6\x6f\xf9\x4f\x87\xfd\xdb\x5f\x4f\x76\x17\x31\x67\xf2\x37\xa2\x5a\x55\x63\x6f\x3d\x9a\xb1\xb7\x54\x31\xf6\x2c\x6c\x49\x97\xf1\x1c\x31\x3c\x59\x80\x9f\x3f\x5e\xeb\x7b\x56\x87\xe0\x32\xf2\x46\x00\x0c\xc5\x8c\x32\xfc\x2d\xbe\x84\xe9\x16\x91\x7a\xfc\xd9\xfd\x30\x3b\x9d\x7d\xf1\x7f\x7f\x19\x7c\x7c\x3f\x39\xeb\x7b\x17\xe8\x36\x70\x07\xff\x38\x89\xf9\x73\x24\xf7\xb2\x57\x94\x4c\x3c\xec\x88\x1a\xbc\xda\xdd\x5f\x0b\xaf\x4c\x30\x76\x5e\x99\x2d\x4c\x11\xd2\x47\x7e\xb4\xe6\xc1\x1c\x40\x4f\xd9\x46\xea\x64\x4a\x29\x1f\xf6\x6f\x3f\x75\x3f\xe0\xd3\xdb\x6f\xb7\xbf\xbf\xfa\xf6\xf1\x3d\x3a\xeb\xd3\x4f\x68\xe6\xee\x9e\x46\x6c\x28\xde\x6f\x6a\x23\xfd\x68\x2d\x94\x1f\x2d\x23\xfc\xc8\x2a\x23\xe9\xf5\xf4\x28\x3b\x68\x61\xca\xd1\xe9\xf9\xfc\xf5\xd1\xe7\xb7\xbf\x7e\xda\xff\x34\x9d\x4d\xde\x1e\x4d\xdf\x5c\xf2\x9f\xe6\xa7\x1f\x13\x5a\x6b\x2b\x8b\xc7\xa3\xd8\xdc\x05\xd5\x98\xc9\xbd\x1d\x40\x5a\x07\x5c\xfa\x4d\xef\x5e\xbd\x6d\x9f\xfe\xde\x3e\x1a\x46\x97\x7c\xc8\x25\xa4\xaf\xf2\x48\xdb\xa0\xaf\xa2\x1d\xed\x7d\x30\xc0\xed\x1e\xfe\xda\xdd\xf5\x88\xeb\xf9\x77\xdd\xbb\x89\x73\xc0\xb1\x80\x7b\xdc\xfb\x3c\x3f\x34\x9d\x90\x89\xf1\x43\xf4\x92\x0f\xbd\xe9\x9e\x7b\x78\x78\xd7\xf5\x98\xe3\xce\x07\xd3\x03\xe8\x8d\x0f\xb8\x37\x99\x92\xcf\xbb\xee\x6c\xcc\x3f\xff\xe5\xff\xfd\xf5\xf4\xf7\xeb\xcb\x63\xf0\x5f\x9a\xe2\x8e\xc2\xf8\x05\x76\x11\x11\x72\xce\xcc\x08\x01\xe6\x60\x7b\xd0\x1d\x6c\x3f\x57\xbc\x50\x7f\xbe\x3a\xff\x70\x75\x7d\x7a\x79\xa5\x99\x21\x3f\xaa\x4c\x77\x32\xb1\x20\x05\xa4\xda\xf7\xa6\x7b\x94\xed\x75\xe7\x38\xec\x1e\x50\x24\xa7\x6d\xc6\x6e\x9d\xfe\xbe\x3b\x9d\x88\xcf\x3d\xe8\x6c\x9b\x9b\x6c\x94\x3c\x56\xbd\x2a\x89\x30\xf4\xed\xdf\x2a\xf4\xc9\x35\xff\xc8\x16\xfb\x84\xdf\x8d\xfb\xfc\xc2\x7f\xfd\x79\x6f\xfc\x7b\x70\x72\xf0\x0a\xb6\xb6\xfe\x2f\x00\x00\xff\xff\xaf\x21\xd3\x0b\x60\xd3\x00\x00") func connector_mgmtYamlBytes() ([]byte, error) { return bindataRead( @@ -93,7 +93,7 @@ func connector_mgmtYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "connector_mgmt.yaml", size: 54904, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + info := bindataFileInfo{name: "connector_mgmt.yaml", size: 54112, mode: os.FileMode(420), modTime: time.Unix(1, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/internal/connector/internal/handlers/connector_admin.go b/internal/connector/internal/handlers/connector_admin.go index 5bd9ab644..e656958a3 100644 --- a/internal/connector/internal/handlers/connector_admin.go +++ b/internal/connector/internal/handlers/connector_admin.go @@ -227,8 +227,8 @@ func (h *ConnectorAdminHandler) CreateConnectorNamespace(writer http.ResponseWri ctx := request.Context() connectorNamespace := presenters.ConvertConnectorNamespaceWithTenantRequest(&resource) - if connectorNamespace.TenantUserId != nil { - connectorNamespace.Owner = *connectorNamespace.TenantUserId + if connectorNamespace.TenantUser != nil { + connectorNamespace.Owner = connectorNamespace.TenantUser.ID } else { // NOTE: admin user is owner claims, err := auth.GetClaimsFromContext(ctx) diff --git a/internal/connector/internal/handlers/connector_agent.go b/internal/connector/internal/handlers/connector_agent.go index ecb9c7cad..d332ef33a 100644 --- a/internal/connector/internal/handlers/connector_agent.go +++ b/internal/connector/internal/handlers/connector_agent.go @@ -232,6 +232,7 @@ func (h *ConnectorClusterHandler) GetDeployment(w http.ResponseWriter, r *http.R handlers.Validation("deployment_id", &deploymentId, handlers.MinLen(1), handlers.MaxLen(maxConnectorIdLength)), }, Action: func() (i interface{}, serviceError *errors.ServiceError) { + var resource dbapi.ConnectorDeployment resource, err := h.Service.GetDeployment(r.Context(), deploymentId) if err != nil { return nil, err diff --git a/internal/connector/internal/handlers/connector_cluster.go b/internal/connector/internal/handlers/connector_cluster.go index 649a1ae2f..c306d0d10 100644 --- a/internal/connector/internal/handlers/connector_cluster.go +++ b/internal/connector/internal/handlers/connector_cluster.go @@ -32,13 +32,14 @@ var ( type ConnectorClusterHandler struct { di.Inject - Bus signalbus.SignalBus - Service services.ConnectorClusterService - Keycloak coreservices.KafkaKeycloakService - ConnectorTypes services.ConnectorTypesService - Vault vault.VaultService - KeycloakConfig *keycloak.KeycloakConfig - ServerConfig *server.ServerConfig + Bus signalbus.SignalBus + Service services.ConnectorClusterService + Keycloak coreservices.KafkaKeycloakService + ConnectorTypes services.ConnectorTypesService + ConnectorNamespace services.ConnectorNamespaceService + Vault vault.VaultService + KeycloakConfig *keycloak.KeycloakConfig + ServerConfig *server.ServerConfig } func NewConnectorClusterHandler(handler ConnectorClusterHandler) *ConnectorClusterHandler { diff --git a/internal/connector/internal/handlers/connector_validation.go b/internal/connector/internal/handlers/connector_validation.go index 3572ffed1..6da4718bf 100644 --- a/internal/connector/internal/handlers/connector_validation.go +++ b/internal/connector/internal/handlers/connector_validation.go @@ -14,9 +14,6 @@ import ( func validateConnectorRequest(connectorTypesService services.ConnectorTypesService, resource *public.ConnectorRequest, tid string) handlers.Validate { return connectorValidationFunction(connectorTypesService, &resource.ConnectorTypeId, &resource.Channel, &resource.Connector, tid) } -func validateConnector(connectorTypesService services.ConnectorTypesService, resource *public.Connector, tid string) handlers.Validate { - return connectorValidationFunction(connectorTypesService, &resource.ConnectorTypeId, &resource.Channel, &resource.Connector, tid) -} func connectorValidationFunction(connectorTypesService services.ConnectorTypesService, connectorTypeId *string, channel *public.Channel, connectorConfiguration *map[string]interface{}, tid string) handlers.Validate { return func() *errors.ServiceError { diff --git a/internal/connector/internal/handlers/connectors.go b/internal/connector/internal/handlers/connectors.go index bd89a9eff..73af214af 100644 --- a/internal/connector/internal/handlers/connectors.go +++ b/internal/connector/internal/handlers/connectors.go @@ -35,13 +35,16 @@ var ( type ConnectorsHandler struct { connectorsService services.ConnectorsService connectorTypesService services.ConnectorTypesService + namespaceService services.ConnectorNamespaceService vaultService vault.VaultService } -func NewConnectorsHandler(connectorsService services.ConnectorsService, connectorTypesService services.ConnectorTypesService, vaultService vault.VaultService) *ConnectorsHandler { +func NewConnectorsHandler(connectorsService services.ConnectorsService, connectorTypesService services.ConnectorTypesService, + namespaceService services.ConnectorNamespaceService, vaultService vault.VaultService) *ConnectorsHandler { return &ConnectorsHandler{ connectorsService: connectorsService, connectorTypesService: connectorTypesService, + namespaceService: namespaceService, vaultService: vaultService, } } @@ -63,8 +66,9 @@ func (h ConnectorsHandler) Create(w http.ResponseWriter, r *http.Request) { handlers.Validation("service_account.client_secret", &resource.ServiceAccount.ClientSecret, handlers.MinLen(1)), handlers.Validation("connector_type_id", &resource.ConnectorTypeId, handlers.MinLen(1), handlers.MaxLen(maxConnectorTypeIdLength)), handlers.Validation("desired_state", (*string)(&resource.DesiredState), handlers.WithDefault("ready"), handlers.IsOneOf(dbapi.ValidDesiredStates...)), - handlers.Validation("deployment_location.kind", &resource.DeploymentLocation.Kind, handlers.IsOneOf("addon")), validateConnectorRequest(h.connectorTypesService, &resource, tid), + handlers.Validation("deployment_location.namespace_id", &resource.DeploymentLocation.NamespaceId, + handlers.MaxLen(maxConnectorNamespaceIdLength), validateNamespaceID(h.namespaceService, r.Context())), }, Action: func() (interface{}, *errors.ServiceError) { @@ -193,9 +197,7 @@ func (h ConnectorsHandler) Patch(w http.ResponseWriter, r *http.Request) { handlers.Validation("connector_type_id", &resource.ConnectorTypeId, handlers.MinLen(1), handlers.MaxLen(maxKafkaNameLength)), // handlers.Validation("kafka_id", &resource.Metadata.KafkaId, handlers.MinLen(1), handlers.MaxLen(maxKafkaNameLength)), handlers.Validation("service_account.client_id", &resource.ServiceAccount.ClientId, handlers.MinLen(1)), - handlers.Validation("deployment_location.kind", &resource.DeploymentLocation.Kind, handlers.IsOneOf("addon")), - handlers.Validation("desired_state", (*string)(&resource.DesiredState), handlers.IsOneOf(dbapi.ValidDesiredStates...)), - validateConnector(h.connectorTypesService, &resource, connectorTypeId), + handlers.Validation("deployment_location.namespace_id", &resource.DeploymentLocation.NamespaceId, handlers.MaxLen(maxConnectorNamespaceIdLength)), } for _, v := range validates { diff --git a/internal/connector/internal/handlers/namespace_validation.go b/internal/connector/internal/handlers/namespace_validation.go new file mode 100644 index 000000000..7d8bbf728 --- /dev/null +++ b/internal/connector/internal/handlers/namespace_validation.go @@ -0,0 +1,20 @@ +package handlers + +import ( + "context" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/services" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/errors" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/handlers" +) + +func validateNamespaceID(namespaceService services.ConnectorNamespaceService, ctx context.Context) handlers.ValidateOption { + + return func(name string, value *string) *errors.ServiceError { + if value != nil && *value != "" { + if _, err := namespaceService.Get(ctx, *value); err != nil { + return errors.BadRequest("%s is not valid: %s", name, err) + } + } + return nil + } +} diff --git a/internal/connector/internal/migrations/202202220000_add_connector_namespace_deployment.go b/internal/connector/internal/migrations/202202220000_add_connector_namespace_deployment.go new file mode 100644 index 000000000..a1220f971 --- /dev/null +++ b/internal/connector/internal/migrations/202202220000_add_connector_namespace_deployment.go @@ -0,0 +1,56 @@ +package migrations + +// Migrations should NEVER use types from other packages. Types can change +// and then migrations run on a _new_ database will fail or behave unexpectedly. +// Instead of importing types, always re-create the type in the migration, as +// is done here, even though the same type is defined in pkg/api + +import ( + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/db" + "github.com/go-gormigrate/gormigrate/v2" +) + +func addConnectorNamespaceDeployment(migrationId string) *gormigrate.Migration { + + return db.CreateMigrationFromActions(migrationId, + // replace column cluster_id with namespace_id in connectors + db.ExecAction(`ALTER TABLE connectors DROP COLUMN addon_cluster_id`, + `ALTER TABLE connectors ADD addon_cluster_id text`), + db.ExecAction(`ALTER TABLE connectors ADD namespace_id text`, + `ALTER TABLE connectors DROP COLUMN namespace_id`), + db.ExecAction( + "ALTER TABLE connectors ADD CONSTRAINT fk_connectors_namespace_id "+ + "FOREIGN KEY (namespace_id) REFERENCES connector_namespaces(id)", + "ALTER TABLE connectors DROP CONSTRAINT IF EXISTS fk_connectors_namespace_id"), + db.ExecAction( + "CREATE INDEX ON connectors(namespace_id)", + "DROP INDEX IF EXISTS connectors_namespace_id_idx"), + + // replace column cluster_id with namespace_id in connector_statuses + db.ExecAction(`ALTER TABLE connector_statuses DROP COLUMN cluster_id`, + `ALTER TABLE connector_statuses ADD cluster_id text`), + db.ExecAction(`ALTER TABLE connector_statuses ADD namespace_id text`, + `ALTER TABLE connector_statuses DROP COLUMN namespace_id`), + db.ExecAction( + "ALTER TABLE connector_statuses ADD CONSTRAINT fk_connector_statuses_namespace_id "+ + "FOREIGN KEY (namespace_id) REFERENCES connector_namespaces(id)", + "ALTER TABLE connector_statuses DROP CONSTRAINT IF EXISTS fk_connector_statuses_namespace_id"), + db.ExecAction( + "CREATE INDEX ON connector_statuses(namespace_id)", + "DROP INDEX IF EXISTS connector_statuses_namespace_id_idx"), + + // add column namespace_id in connector_deployments, keeping cluster_id for agent API + db.ExecAction(`ALTER TABLE connector_deployments ADD namespace_id text not null`, + `ALTER TABLE connector_deployments DROP COLUMN namespace_id`), + db.ExecAction( + "ALTER TABLE connector_deployments DROP CONSTRAINT IF EXISTS fk_connector_deployments_namespace_id", + ""), + db.ExecAction( + "ALTER TABLE connector_deployments ADD CONSTRAINT fk_connector_deployments_namespace_id "+ + "FOREIGN KEY (namespace_id) REFERENCES connector_namespaces(id)", + "ALTER TABLE connector_deployments DROP CONSTRAINT IF EXISTS fk_connector_deployments_namespace_id"), + db.ExecAction( + "CREATE INDEX ON connector_deployments(namespace_id)", + "DROP INDEX IF EXISTS connector_deployments_namespace_id_idx"), + ) +} diff --git a/internal/connector/internal/migrations/migrations.go b/internal/connector/internal/migrations/migrations.go index 348188d4e..842295319 100644 --- a/internal/connector/internal/migrations/migrations.go +++ b/internal/connector/internal/migrations/migrations.go @@ -29,6 +29,7 @@ var migrations = []*gormigrate.Migration{ addConnectorTypeCapabilitiesTable("202202040000"), addClientId("202202030000"), addConnectorNamespaceTables("202202070000"), + addConnectorNamespaceDeployment("202202220000"), } func New(dbConfig *db.DatabaseConfig) (*db.Migration, func(), error) { diff --git a/internal/connector/internal/presenters/connector.go b/internal/connector/internal/presenters/connector.go index 18fff8cab..cdec62662 100644 --- a/internal/connector/internal/presenters/connector.go +++ b/internal/connector/internal/presenters/connector.go @@ -16,12 +16,16 @@ func ConvertConnector(from public.Connector) (*dbapi.Connector, *errors.ServiceE return nil, errors.BadRequest("invalid connector spec: %v", err) } + var namespaceId *string + if from.DeploymentLocation.NamespaceId != "" { + namespaceId = &from.DeploymentLocation.NamespaceId + } return &dbapi.Connector{ Meta: api.Meta{ ID: from.Id, }, - TargetKind: from.DeploymentLocation.Kind, - AddonClusterId: from.DeploymentLocation.ClusterId, + TargetKind: dbapi.AddonTargetKind, + NamespaceId: namespaceId, Name: from.Name, Owner: from.Owner, Version: from.ResourceVersion, @@ -54,6 +58,11 @@ func PresentConnector(from *dbapi.Connector) (public.Connector, *errors.ServiceE return public.Connector{}, errors.BadRequest("invalid connector spec: %v", err) } + namespaceId := "" + if from.NamespaceId != nil { + namespaceId = *from.NamespaceId + } + reference := PresentReference(from.ID, from) return public.Connector{ Id: reference.Id, @@ -67,8 +76,7 @@ func PresentConnector(from *dbapi.Connector) (public.Connector, *errors.ServiceE ResourceVersion: from.Version, DeploymentLocation: public.DeploymentLocation{ - Kind: from.TargetKind, - ClusterId: from.AddonClusterId, + NamespaceId: namespaceId, }, ConnectorTypeId: from.ConnectorTypeId, Connector: spec, diff --git a/internal/connector/internal/presenters/connector_available_operator_upgrade.go b/internal/connector/internal/presenters/connector_available_operator_upgrade.go index 37a82ba44..ceb4a00b5 100644 --- a/internal/connector/internal/presenters/connector_available_operator_upgrade.go +++ b/internal/connector/internal/presenters/connector_available_operator_upgrade.go @@ -9,6 +9,7 @@ func PresentConnectorAvailableOperatorUpgrade(req *dbapi.ConnectorDeploymentOper return &private.ConnectorAvailableOperatorUpgrade{ ConnectorId: req.ConnectorID, ConnectorTypeId: req.ConnectorTypeId, + Namespace: req.Namespace, Channel: req.Channel, Operator: private.ConnectorAvailableOperatorUpgradeOperator{ AssignedId: req.Operator.Assigned.Id, @@ -21,6 +22,7 @@ func ConvertConnectorAvailableOperatorUpgrade(req *private.ConnectorAvailableOpe return &dbapi.ConnectorDeploymentOperatorUpgrade{ ConnectorID: req.ConnectorId, ConnectorTypeId: req.ConnectorTypeId, + Namespace: req.Namespace, Channel: req.Channel, Operator: &dbapi.ConnectorOperatorUpgrade{ Assigned: dbapi.ConnectorOperator{ diff --git a/internal/connector/internal/presenters/connector_available_type_upgrade.go b/internal/connector/internal/presenters/connector_available_type_upgrade.go index 64f9f106f..12f5ac058 100644 --- a/internal/connector/internal/presenters/connector_available_type_upgrade.go +++ b/internal/connector/internal/presenters/connector_available_type_upgrade.go @@ -9,6 +9,7 @@ func PresentConnectorAvailableTypeUpgrade(req *dbapi.ConnectorDeploymentTypeUpgr return &private.ConnectorAvailableTypeUpgrade{ ConnectorId: req.ConnectorID, ConnectorTypeId: req.ConnectorTypeId, + Namespace: req.Namespace, Channel: req.Channel, ShardMetadata: private.ConnectorAvailableTypeUpgradeShardMetadata{ AssignedId: req.ShardMetadata.AssignedId, @@ -21,6 +22,7 @@ func ConvertConnectorAvailableTypeUpgrade(req *private.ConnectorAvailableTypeUpg return &dbapi.ConnectorDeploymentTypeUpgrade{ ConnectorID: req.ConnectorId, ConnectorTypeId: req.ConnectorTypeId, + Namespace: req.Namespace, Channel: req.Channel, ShardMetadata: &dbapi.ConnectorTypeUpgrade{ AssignedId: req.ShardMetadata.AssignedId, diff --git a/internal/connector/internal/presenters/connector_deployment.go b/internal/connector/internal/presenters/connector_deployment.go index a0fb1d741..d6b7e1ab3 100644 --- a/internal/connector/internal/presenters/connector_deployment.go +++ b/internal/connector/internal/presenters/connector_deployment.go @@ -38,6 +38,8 @@ func PresentConnectorDeployment(from dbapi.ConnectorDeployment) (private.Connect Spec: private.ConnectorDeploymentSpec{ ConnectorId: from.ConnectorID, OperatorId: from.OperatorID, + NamespaceId: from.NamespaceID, + NamespaceName: from.NamespaceName, ConnectorResourceVersion: from.ConnectorVersion, }, Status: private.ConnectorDeploymentStatus{ diff --git a/internal/connector/internal/presenters/connector_namespace.go b/internal/connector/internal/presenters/connector_namespace.go index 2f2d1a90d..8325f0eed 100644 --- a/internal/connector/internal/presenters/connector_namespace.go +++ b/internal/connector/internal/presenters/connector_namespace.go @@ -8,6 +8,12 @@ import ( "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/api" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/db" "strings" + "time" +) + +const ( + UserKind string = "user" + OrganisationKind string = "organisation" ) func ConvertConnectorNamespaceRequest(namespaceRequest *public.ConnectorNamespaceRequest) *dbapi.ConnectorNamespace { @@ -53,21 +59,23 @@ func ConvertConnectorNamespaceWithTenantRequest(namespaceRequest *private.Connec Name: namespaceRequest.Name, ClusterId: namespaceRequest.ClusterId, } - if namespaceRequest.Tenant.UserId != "" { + switch namespaceRequest.Tenant.Kind { + case UserKind: result.TenantUserId = &namespaceRequest.Tenant.UserId result.TenantUser = &dbapi.ConnectorTenantUser{ Model: db.Model{ ID: *result.TenantUserId, }, } - } - if namespaceRequest.Tenant.OrganisationId != "" { + case OrganisationKind: result.TenantOrganisationId = &namespaceRequest.Tenant.OrganisationId result.TenantOrganisation = &dbapi.ConnectorTenantOrganisation{ Model: db.Model{ ID: *result.TenantOrganisationId, }, } + default: + // ignore, should have been validated earlier } result.Annotations = make([]dbapi.ConnectorNamespaceAnnotation, len(namespaceRequest.Annotations)) for i, annotation := range namespaceRequest.Annotations { @@ -102,17 +110,16 @@ func PresentConnectorNamespace(namespace *dbapi.ConnectorNamespace) public.Conne Tenant: public.ConnectorNamespaceTenant{}, Annotations: annotations, } - if namespace.TenantUserId != nil { - result.Tenant.Kind = "user" - result.Tenant.UserId = *namespace.TenantUserId + if namespace.TenantUser != nil { + result.Tenant.Kind = UserKind + result.Tenant.UserId = namespace.TenantUser.ID } - if namespace.TenantOrganisationId != nil { - result.Tenant.Kind = "organisation" - result.Tenant.OrganisationId = *namespace.TenantOrganisationId + if namespace.TenantOrganisation != nil { + result.Tenant.Kind = OrganisationKind + result.Tenant.OrganisationId = namespace.TenantOrganisation.ID } if namespace.Expiration != nil { - bytes, _ := json.Marshal(namespace.Expiration) - result.Expiration = strings.Trim(string(bytes), "\"") + result.Expiration = getTimestamp(*namespace.Expiration) } return result @@ -141,18 +148,22 @@ func PresentPrivateConnectorNamespace(namespace *dbapi.ConnectorNamespace) priva Tenant: private.ConnectorNamespaceTenant{}, Annotations: annotations, } - if namespace.TenantUserId != nil { - result.Tenant.Kind = "user" - result.Tenant.UserId = *namespace.TenantUserId + if namespace.TenantUser != nil { + result.Tenant.Kind = UserKind + result.Tenant.UserId = namespace.TenantUser.ID } if namespace.TenantOrganisationId != nil { - result.Tenant.Kind = "organisation" - result.Tenant.OrganisationId = *namespace.TenantOrganisationId + result.Tenant.Kind = OrganisationKind + result.Tenant.OrganisationId = namespace.TenantOrganisation.ID } if namespace.Expiration != nil { - bytes, _ := json.Marshal(namespace.Expiration) - result.Expiration = string(bytes) + result.Expiration = getTimestamp(*namespace.Expiration) } return result } + +func getTimestamp(expiration time.Time) string { + bytes, _ := json.Marshal(expiration) + return strings.Trim(string(bytes), "\"") +} diff --git a/internal/connector/internal/presenters/connector_request.go b/internal/connector/internal/presenters/connector_request.go index fada7c723..51b27c147 100644 --- a/internal/connector/internal/presenters/connector_request.go +++ b/internal/connector/internal/presenters/connector_request.go @@ -15,9 +15,13 @@ func ConvertConnectorRequest(from public.ConnectorRequest) (*dbapi.Connector, *e return nil, errors.BadRequest("invalid connector spec: %v", err) } + namespaceId := &from.DeploymentLocation.NamespaceId + if *namespaceId == "" { + namespaceId = nil + } return &dbapi.Connector{ - TargetKind: from.DeploymentLocation.Kind, - AddonClusterId: from.DeploymentLocation.ClusterId, + TargetKind: dbapi.AddonTargetKind, + NamespaceId: namespaceId, Name: from.Name, ConnectorTypeId: from.ConnectorTypeId, ConnectorSpec: spec, diff --git a/internal/connector/internal/services/connector_cluster.go b/internal/connector/internal/services/connector_cluster.go index 328735e8a..54b061bb8 100644 --- a/internal/connector/internal/services/connector_cluster.go +++ b/internal/connector/internal/services/connector_cluster.go @@ -8,6 +8,7 @@ import ( goerrors "errors" "fmt" "github.com/golang/glog" + "math/rand" "reflect" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/dbapi" @@ -37,7 +38,7 @@ type ConnectorClusterService interface { GetConnectorWithBase64Secrets(ctx context.Context, resource dbapi.ConnectorDeployment) (dbapi.Connector, *errors.ServiceError) ListConnectorDeployments(ctx context.Context, id string, listArgs *services.ListArguments, gtVersion int64) (dbapi.ConnectorDeploymentList, *api.PagingMeta, *errors.ServiceError) UpdateConnectorDeploymentStatus(ctx context.Context, status dbapi.ConnectorDeploymentStatus) *errors.ServiceError - FindReadyCluster(owner string, orgId string, group string) (*dbapi.ConnectorCluster, *errors.ServiceError) + FindReadyNamespace(owner string, orgId string, namespaceId *string) (*dbapi.ConnectorNamespace, *errors.ServiceError) GetDeploymentByConnectorId(ctx context.Context, connectorID string) (dbapi.ConnectorDeployment, *errors.ServiceError) GetDeployment(ctx context.Context, id string) (dbapi.ConnectorDeployment, *errors.ServiceError) GetAvailableDeploymentTypeUpgrades(listArgs *services.ListArguments) (dbapi.ConnectorDeploymentTypeUpgradeList, *api.PagingMeta, *errors.ServiceError) @@ -52,23 +53,35 @@ var _ ConnectorClusterService = &connectorClusterService{} var _ auth.AuthAgentService = &connectorClusterService{} type connectorClusterService struct { - connectionFactory *db.ConnectionFactory - bus signalbus.SignalBus - connectorTypesService ConnectorTypesService - vaultService vault.VaultService - keycloakService services.KafkaKeycloakService - connectorsService ConnectorsService + connectionFactory *db.ConnectionFactory + bus signalbus.SignalBus + connectorTypesService ConnectorTypesService + vaultService vault.VaultService + keycloakService services.KafkaKeycloakService + connectorsService ConnectorsService + connectorNamespaceService ConnectorNamespaceService + deploymentColumns []string } func NewConnectorClusterService(connectionFactory *db.ConnectionFactory, bus signalbus.SignalBus, vaultService vault.VaultService, - connectorTypesService ConnectorTypesService, connectorsService ConnectorsService, keycloakService services.KafkaKeycloakService) *connectorClusterService { + connectorTypesService ConnectorTypesService, connectorsService ConnectorsService, + keycloakService services.KafkaKeycloakService, connectorNamespaceService ConnectorNamespaceService) *connectorClusterService { + types, _ := connectionFactory.New().Migrator().ColumnTypes(&dbapi.ConnectorDeployment{}) + nTypes := len(types) + columnNames := make([]string, nTypes+1) + for i, columnType := range types { + columnNames[i] = "connector_deployments." + columnType.Name() + } + columnNames[nTypes] = "connector_namespaces.name AS namespace_name" return &connectorClusterService{ - connectionFactory: connectionFactory, - bus: bus, - connectorTypesService: connectorTypesService, - vaultService: vaultService, - connectorsService: connectorsService, - keycloakService: keycloakService, + connectionFactory: connectionFactory, + bus: bus, + connectorTypesService: connectorTypesService, + vaultService: vaultService, + connectorsService: connectorsService, + keycloakService: keycloakService, + connectorNamespaceService: connectorNamespaceService, + deploymentColumns: columnNames, } } @@ -118,11 +131,40 @@ func (k *connectorClusterService) Create(ctx context.Context, resource *dbapi.Co if err := dbConn.Save(resource).Error; err != nil { return errors.GeneralError("failed to create connector: %v", err) } + + // create a default namespace for the new cluster + if err := k.connectorNamespaceService.CreateDefaultNamespace(ctx, resource); err != nil { + return err + } // TODO: increment connector cluster metrics // metrics.IncreaseStatusCountMetric(constants.KafkaRequestStatusAccepted.String()) return nil } +func filterClusterToOwnerOrOrg(ctx context.Context, dbConn *gorm.DB) (*gorm.DB, *errors.ServiceError) { + + claims, err := auth.GetClaimsFromContext(ctx) + if err != nil { + return dbConn, errors.Unauthenticated("user not authenticated") + } + owner := auth.GetUsernameFromClaims(claims) + if owner == "" { + return dbConn, errors.Unauthenticated("user not authenticated") + } + + orgId := auth.GetOrgIdFromClaims(claims) + filterByOrganisationId := auth.GetFilterByOrganisationFromContext(ctx) + + // filter by organisationId if a user is part of an organisation and is not allowed as a service account + if filterByOrganisationId { + // include namespaces where either user or organisation is the tenant + dbConn = dbConn.Where("organisation_id = ?", orgId) + } else { + dbConn = dbConn.Where("owner = ?", owner) + } + return dbConn, nil +} + // Get gets a connector by id from the database func (k *connectorClusterService) Get(ctx context.Context, id string) (dbapi.ConnectorCluster, *errors.ServiceError) { @@ -131,7 +173,7 @@ func (k *connectorClusterService) Get(ctx context.Context, id string) (dbapi.Con dbConn = dbConn.Where("id = ?", id) var err *errors.ServiceError - dbConn, err = filterToOwnerOrOrg(ctx, dbConn) + dbConn, err = filterClusterToOwnerOrOrg(ctx, dbConn) if err != nil { return resource, err } @@ -195,12 +237,37 @@ func (k *connectorClusterService) Delete(ctx context.Context, id string) *errors } } + // Delete all namespaces assigned to the cluster.. + dbConn = k.connectionFactory.New() + { + rows, err := dbConn. + Model(&dbapi.ConnectorNamespace{}). + Select("id"). + Where("cluster_id = ?", id). + Rows() + if err != nil { + return errors.GeneralError("unable find namespaces of cluster %s: %s", id, err) + } + defer rows.Close() + for rows.Next() { + namespace := dbapi.ConnectorNamespace{} + err := dbConn.ScanRows(rows, &namespace) + if err != nil { + return errors.GeneralError("Unable to scan connector namespace: %s", err) + } + if err := dbConn.Delete(&namespace).Error; err != nil { + return errors.GeneralError("failed to delete connector namespace: %s", err) + } + } + } + // Clear the cluster from any connectors that were using it. { rows, err := dbConn. Model(&dbapi.Connector{}). - Select("id"). - Where("addon_cluster_id = ?", id). + Select("connectors.id"). + Joins("INNER JOIN connector_namespaces ON connector_namespaces.id = connectors.namespace_id"). + Where("connector_namespaces.cluster_id = ?", id). Rows() if err != nil { return errors.GeneralError("unable find connector using cluster %s: %s", id, err) @@ -213,7 +280,7 @@ func (k *connectorClusterService) Delete(ctx context.Context, id string) *errors return errors.GeneralError("Unable to scan connector: %s", err) } - if err := dbConn.Model(&connector).Update("addon_cluster_id", "").Error; err != nil { + if err := dbConn.Model(&connector).Update("namespace_id", nil).Error; err != nil { return errors.GeneralError("failed to update connector: %s", err) } @@ -288,7 +355,7 @@ func (k *connectorClusterService) List(ctx context.Context, listArgs *services.L return nil, nil, err } if !admin { - dbConn, err = filterToOwnerOrOrg(ctx, dbConn) + dbConn, err = filterClusterToOwnerOrOrg(ctx, dbConn) if err != nil { return nil, nil, err } @@ -437,9 +504,10 @@ func (k *connectorClusterService) ListConnectorDeployments(ctx context.Context, Size: listArgs.Size, } - dbConn = dbConn.Where("cluster_id = ?", id) + dbConn = dbConn. + Where("connector_deployments.cluster_id = ?", id) if gtVersion != 0 { - dbConn = dbConn.Where("version > ?", gtVersion) + dbConn = dbConn.Where("connector_deployments.version > ?", gtVersion) } // set total, limit and paging (based on https://gitlab.cee.redhat.com/service/api-guidelines#user-content-paging) @@ -453,7 +521,10 @@ func (k *connectorClusterService) ListConnectorDeployments(ctx context.Context, dbConn = dbConn.Offset((pagingMeta.Page - 1) * pagingMeta.Size).Limit(pagingMeta.Size) // default the order by version - dbConn = dbConn.Order("version") + dbConn = dbConn.Order("connector_deployments.version") + + // add namespace_name column + dbConn = k.selectDeploymentColumns(dbConn) // execute query if err := dbConn.Find(&resourceList).Error; err != nil { @@ -463,6 +534,12 @@ func (k *connectorClusterService) ListConnectorDeployments(ctx context.Context, return resourceList, pagingMeta, nil } +func (k *connectorClusterService) selectDeploymentColumns(dbConn *gorm.DB) *gorm.DB { + // join connector_namespaces for namespace name + return dbConn.Select(k.deploymentColumns). + Joins("INNER JOIN connector_namespaces ON connector_namespaces.id = namespace_id") +} + func (k *connectorClusterService) UpdateConnectorDeploymentStatus(ctx context.Context, deploymentStatus dbapi.ConnectorDeploymentStatus) *errors.ServiceError { dbConn := k.connectionFactory.New() @@ -516,25 +593,52 @@ func (k *connectorClusterService) UpdateConnectorDeploymentStatus(ctx context.Co return nil } -func (k *connectorClusterService) FindReadyCluster(owner string, orgId string, connectorClusterId string) (*dbapi.ConnectorCluster, *errors.ServiceError) { +func (k *connectorClusterService) FindReadyNamespace(owner string, orgID string, namespaceID *string) (*dbapi.ConnectorNamespace, *errors.ServiceError) { dbConn := k.connectionFactory.New() - var resource dbapi.ConnectorCluster + var namespaces dbapi.ConnectorNamespaceList - dbConn = dbConn.Where("id = ? AND status_phase = ?", connectorClusterId, dbapi.ConnectorClusterPhaseReady) + if namespaceID != nil { + dbConn = dbConn.Where("connector_namespaces.id = ?", namespaceID) + } + dbConn = dbConn.Joins("INNER JOIN connector_clusters"+ + " ON cluster_id = connector_clusters.id and connector_clusters.status_phase = ?", dbapi.ConnectorClusterPhaseReady) - if orgId != "" { - dbConn = dbConn.Where("organisation_id = ?", orgId) + if orgID != "" { + dbConn = dbConn.Where("tenant_organisation_id = ? or tenant_user_id = ?", orgID, owner) } else { - dbConn = dbConn.Where("owner = ?", owner) + dbConn = dbConn.Where("tenant_owner_id = ?", owner) } - if err := dbConn.First(&resource).Error; err != nil { - if goerrors.Is(err, gorm.ErrRecordNotFound) { - return nil, nil + if err := dbConn.Find(&namespaces).Error; err != nil { + return nil, errors.GeneralError("failed to query ready connector namespace: %v", err.Error()) + } + + n := len(namespaces) + if n == 1 { + return namespaces[0], nil + } else if n > 1 { + // prioritise and select from multiple namespaces + ownerNamespaces := dbapi.ConnectorNamespaceList{} + orgNamespaces := dbapi.ConnectorNamespaceList{} + for _, namespace := range namespaces { + if namespace.TenantUserId != nil { + ownerNamespaces = append(ownerNamespaces, namespace) + } else { + orgNamespaces = append(orgNamespaces, namespace) + } + } + + // TODO replace with more sophisticated load balancing logic in the future + // prefer owner tenant + n := len(ownerNamespaces) + if n > 0 { + return ownerNamespaces[rand.Intn(n)], nil + } else { + return orgNamespaces[rand.Intn(len(orgNamespaces))], nil } - return nil, errors.GeneralError("failed to query ready addon connector cluster: %v", err.Error()) } - return &resource, nil + + return nil, nil } func Checksum(spec interface{}) (string, error) { @@ -622,17 +726,18 @@ func getSecretsFromVaultAsBase64(resource *dbapi.Connector, cts ConnectorTypesSe func (k *connectorClusterService) GetDeploymentByConnectorId(ctx context.Context, connectorID string) (resource dbapi.ConnectorDeployment, serr *errors.ServiceError) { dbConn := k.connectionFactory.New() - dbConn = dbConn.Where("connector_id = ?", connectorID) + dbConn = k.selectDeploymentColumns(dbConn).Where("connector_id = ?", connectorID) if err := dbConn.First(&resource).Error; err != nil { return resource, services.HandleGetError("Connector deployment", "connector_id", connectorID, err) } return } + func (k *connectorClusterService) GetDeployment(ctx context.Context, id string) (resource dbapi.ConnectorDeployment, serr *errors.ServiceError) { dbConn := k.connectionFactory.New() - dbConn = dbConn.Unscoped().Where("id = ?", id) - if err := dbConn.First(&resource).Error; err != nil { + dbConn = dbConn.Unscoped().Where("connector_deployments.id = ?", id) + if err := k.selectDeploymentColumns(dbConn).First(&resource).Error; err != nil { return resource, services.HandleGetError("Connector deployment", "id", id, err) } @@ -651,6 +756,7 @@ func (k *connectorClusterService) GetAvailableDeploymentTypeUpgrades(listArgs *s ConnectorTypeUpgradeFrom int64 ConnectorTypeUpgradeTo int64 ConnectorTypeID string + Namespace string Channel string } @@ -663,10 +769,12 @@ func (k *connectorClusterService) GetAvailableDeploymentTypeUpgrades(listArgs *s "connector_shard_metadata.id AS connector_type_upgrade_from", "connector_shard_metadata.latest_id AS connector_type_upgrade_to", "connector_shard_metadata.connector_type_id", + "connector_namespaces.name AS namespace", "connector_shard_metadata.channel", ) dbConn = dbConn.Joins("LEFT JOIN connector_shard_metadata ON connector_shard_metadata.id = connector_deployments.connector_type_channel_id") dbConn = dbConn.Joins("LEFT JOIN connector_deployment_statuses ON connector_deployment_statuses.id = connector_deployments.id") + dbConn = dbConn.Joins("LEFT JOIN connector_namespaces ON connector_namespaces.id = connector_deployments.namespace_id") dbConn = dbConn.Where("connector_shard_metadata.latest_id IS NOT NULL") dbConn = dbConn.Or("connector_deployment_statuses.upgrade_available") @@ -687,6 +795,7 @@ func (k *connectorClusterService) GetAvailableDeploymentTypeUpgrades(listArgs *s ConnectorID: r.ConnectorID, DeploymentID: r.DeploymentID, ConnectorTypeId: r.ConnectorTypeID, + Namespace: r.Namespace, Channel: r.Channel, } @@ -768,6 +877,7 @@ func (k *connectorClusterService) GetAvailableDeploymentOperatorUpgrades(listArg ConnectorID string DeploymentID string ConnectorTypeID string + Namespace string Channel string ConnectorOperators api.JSON } @@ -779,11 +889,13 @@ func (k *connectorClusterService) GetAvailableDeploymentOperatorUpgrades(listArg "connector_deployments.connector_id AS connector_id", "connector_deployments.id AS deployment_id", "connector_shard_metadata.connector_type_id", + "connector_namespaces.name AS namespace", "connector_shard_metadata.channel", "connector_deployment_statuses.operators AS connector_operators", ) dbConn = dbConn.Joins("LEFT JOIN connector_shard_metadata ON connector_shard_metadata.id = connector_deployments.connector_type_channel_id") dbConn = dbConn.Joins("LEFT JOIN connector_deployment_statuses ON connector_deployment_statuses.id = connector_deployments.id") + dbConn = dbConn.Joins("LEFT JOIN connector_namespaces ON connector_namespaces.id = connector_deployments.namespace_id") dbConn = dbConn.Where("connector_deployment_statuses.upgrade_available") if err := dbConn.Scan(&results).Error; err != nil { @@ -802,6 +914,7 @@ func (k *connectorClusterService) GetAvailableDeploymentOperatorUpgrades(listArg ConnectorID: r.ConnectorID, DeploymentID: r.DeploymentID, ConnectorTypeId: r.ConnectorTypeID, + Namespace: r.Namespace, Channel: r.Channel, } diff --git a/internal/connector/internal/services/namespace_service.go b/internal/connector/internal/services/connector_namespaces.go similarity index 76% rename from internal/connector/internal/services/namespace_service.go rename to internal/connector/internal/services/connector_namespaces.go index 4e6102feb..fa06ec762 100644 --- a/internal/connector/internal/services/namespace_service.go +++ b/internal/connector/internal/services/connector_namespaces.go @@ -3,7 +3,9 @@ package services import ( "context" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/dbapi" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/public" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/config" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/presenters" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/api" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/db" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/errors" @@ -24,6 +26,7 @@ type ConnectorNamespaceService interface { SetEvalClusterId(request *dbapi.ConnectorNamespace) *errors.ServiceError GetOwnerClusterIds(userId string, ownerClusterIds *[]string) *errors.ServiceError GetOrgClusterIds(userId string, organisationId string, orgClusterIds *[]string) *errors.ServiceError + CreateDefaultNamespace(ctx context.Context, connectorCluster *dbapi.ConnectorCluster) *errors.ServiceError } var _ ConnectorNamespaceService = &connectorNamespaceService{} @@ -140,7 +143,7 @@ func (k *connectorNamespaceService) Create(ctx context.Context, request *dbapi.C return errors.GeneralError("failed to create connector namespace: %v", err) } // reload namespace to get version update - if err := dbConn.Find(request).Error; err != nil { + if err := dbConn.Select("version").First(request).Error; err != nil { return errors.GeneralError("failed to read new connector namespace: %v", err) } @@ -155,6 +158,10 @@ func (k *connectorNamespaceService) Update(ctx context.Context, request *dbapi.C if err := dbConn.Save(request).Error; err != nil { return errors.GeneralError("failed to update connector namespace: %v", err) } + // reload namespace to get version update + if err := dbConn.Select("version").First(request).Error; err != nil { + return errors.GeneralError("failed to read updated connector namespace: %v", err) + } // TODO use signal bus and a namespace worker to sync connectors in updated namespace @@ -170,7 +177,7 @@ func (k *connectorNamespaceService) Get(ctx context.Context, namespaceID string) ID: namespaceID, }, } - if err := dbConn.Find(result).Error; err != nil { + if err := dbConn.First(result).Error; err != nil { return nil, errors.GeneralError("failed to get connector namespace: %v", err) } @@ -235,7 +242,77 @@ func (k *connectorNamespaceService) Delete(ctx context.Context, namespaceId stri return errors.GeneralError("failed to delete connector namespace: %v", err) } + // Clear the namespace from any connectors that were using it. + // TODO do this asynchronously in a namespace reconciler + dbConn = k.connectionFactory.New() + { + rows, err := dbConn. + Model(&dbapi.Connector{}). + Select("id"). + Where("namespace_id = ?", namespaceId). + Rows() + if err != nil { + return errors.GeneralError("unable find connector using namespace %s: %s", namespaceId, err) + } + defer rows.Close() + for rows.Next() { + connector := dbapi.Connector{} + err := dbConn.ScanRows(rows, &connector) + if err != nil { + return errors.GeneralError("Unable to scan connector: %s", err) + } + + if err := dbConn.Model(&connector).Update("namespace_id", nil).Error; err != nil { + return errors.GeneralError("failed to update connector: %s", err) + } + + status := dbapi.ConnectorStatus{} + status.ID = connector.ID + status.NamespaceID = nil + if err := dbConn.Model(&status).Update("phase", "assigning").Error; err != nil { + return errors.GeneralError("failed to update connector status: %s", err) + } + } + } + // TODO: increment connector namespace metrics // metrics.IncreaseStatusCountMetric(constants.KafkaRequestStatusAccepted.String()) return nil } + +// TODO make this a configurable property in the future +const defaultNamespaceName = "default-connector-namespace" + +func (k *connectorNamespaceService) CreateDefaultNamespace(ctx context.Context, connectorCluster *dbapi.ConnectorCluster) *errors.ServiceError { + namespaceRequest := presenters.ConvertConnectorNamespaceRequest(&public.ConnectorNamespaceRequest{ + Name: defaultNamespaceName, + Annotations: []public.ConnectorNamespaceRequestMetaAnnotations{ + { + Name: "connector_mgmt.api.openshift.com/profile", + Value: "default-profile", + }, + }, + ClusterId: connectorCluster.ID, + }) + + // namespace has the same owner and tenant org as cluster + owner := connectorCluster.Owner + namespaceRequest.Owner = owner + organisationId := connectorCluster.OrganisationId + if organisationId != "" { + namespaceRequest.TenantOrganisationId = &organisationId + namespaceRequest.TenantOrganisation = &dbapi.ConnectorTenantOrganisation{ + Model: db.Model{ + ID: organisationId, + }, + } + } else { + namespaceRequest.TenantUserId = &owner + namespaceRequest.TenantUser = &dbapi.ConnectorTenantUser{ + Model: db.Model{ + ID: owner, + }, + } + } + return k.Create(ctx, namespaceRequest) +} diff --git a/internal/connector/internal/services/connectors.go b/internal/connector/internal/services/connectors.go index b27036316..e5d38a630 100644 --- a/internal/connector/internal/services/connectors.go +++ b/internal/connector/internal/services/connectors.go @@ -39,7 +39,8 @@ type connectorsService struct { connectorTypesService ConnectorTypesService } -func NewConnectorsService(connectionFactory *db.ConnectionFactory, bus signalbus.SignalBus, vaultService vault.VaultService, connectorTypesService ConnectorTypesService) *connectorsService { +func NewConnectorsService(connectionFactory *db.ConnectionFactory, bus signalbus.SignalBus, + vaultService vault.VaultService, connectorTypesService ConnectorTypesService) *connectorsService { return &connectorsService{ connectionFactory: connectionFactory, bus: bus, @@ -93,7 +94,7 @@ func (k *connectorsService) Get(ctx context.Context, id string, tid string) (*db dbConn = dbConn.Preload("Status") var err *errors.ServiceError - dbConn, err = filterToOwnerOrOrg(ctx, dbConn) + dbConn, err = filterConnectorsToOwnerOrOrg(ctx, dbConn, k.connectionFactory) if err != nil { return nil, err } @@ -108,7 +109,8 @@ func (k *connectorsService) Get(ctx context.Context, id string, tid string) (*db return &resource, nil } -func filterToOwnerOrOrg(ctx context.Context, dbConn *gorm.DB) (*gorm.DB, *errors.ServiceError) { +func filterConnectorsToOwnerOrOrg(ctx context.Context, dbConn *gorm.DB, factory *db.ConnectionFactory) (*gorm.DB, *errors.ServiceError) { + claims, err := auth.GetClaimsFromContext(ctx) if err != nil { return dbConn, errors.Unauthenticated("user not authenticated") @@ -123,7 +125,13 @@ func filterToOwnerOrOrg(ctx context.Context, dbConn *gorm.DB) (*gorm.DB, *errors // filter by organisationId if a user is part of an organisation and is not allowed as a service account if filterByOrganisationId { - dbConn = dbConn.Where("organisation_id = ?", orgId) + // unassigned connectors with no namespace_id use owner and org + // assigned connectors use tenant user or organisation + dbConn = dbConn.Where("(namespace_id is null AND (owner = ? or organisation_id = ?)) OR (namespace_id is not null AND namespace_id IN (?))", + owner, + orgId, + factory.New().Table("connector_namespaces").Select("id"). + Where("deleted_at is null AND (tenant_user_id = ? OR tenant_organisation_id = ?)", owner, orgId)) } else { dbConn = dbConn.Where("owner = ?", owner) } @@ -202,7 +210,7 @@ func (k *connectorsService) List(ctx context.Context, kafka_id string, listArgs } var err *errors.ServiceError - dbConn, err = filterToOwnerOrOrg(ctx, dbConn) + dbConn, err = filterConnectorsToOwnerOrOrg(ctx, dbConn, k.connectionFactory) if err != nil { return nil, nil, err } diff --git a/internal/connector/internal/workers/connector_mgr.go b/internal/connector/internal/workers/connector_mgr.go index d364dcea5..7dbe53cbb 100644 --- a/internal/connector/internal/workers/connector_mgr.go +++ b/internal/connector/internal/workers/connector_mgr.go @@ -199,15 +199,17 @@ func (k *ConnectorManager) ReconcileConnectorCatalogEntry(id string, channel str return nil } +//goland:noinspection VacuumSwitchStatement func (k *ConnectorManager) reconcileAssigning(ctx context.Context, connector *dbapi.Connector) error { switch connector.TargetKind { case dbapi.AddonTargetKind: - cluster, err := k.connectorClusterService.FindReadyCluster(connector.Owner, connector.OrganisationId, connector.AddonClusterId) + var namespace *dbapi.ConnectorNamespace + namespace, err := k.connectorClusterService.FindReadyNamespace(connector.Owner, connector.OrganisationId, connector.NamespaceId) if err != nil { - return errors.Wrapf(err, "failed to find cluster for connector request %s", connector.ID) + return errors.Wrapf(err, "failed to find namespace for connector request %s", connector.ID) } - if cluster == nil { + if namespace == nil { // we will try to find a ready cluster again in the next reconcile return nil } @@ -219,10 +221,10 @@ func (k *ConnectorManager) reconcileAssigning(ctx context.Context, connector *db var status = dbapi.ConnectorStatus{} status.ID = connector.ID - status.ClusterID = cluster.ID + status.NamespaceID = &namespace.ID status.Phase = dbapi.ConnectorStatusPhaseAssigned if err = k.connectorService.SaveStatus(ctx, status); err != nil { - return errors.Wrapf(err, "failed to update connector status %s with cluster details", connector.ID) + return errors.Wrapf(err, "failed to update connector status %s with namespace details", status.ID) } deployment := dbapi.ConnectorDeployment{ @@ -230,7 +232,8 @@ func (k *ConnectorManager) reconcileAssigning(ctx context.Context, connector *db ID: api.NewID(), }, ConnectorID: connector.ID, - ClusterID: cluster.ID, + ClusterID: namespace.ClusterId, + NamespaceID: namespace.ID, ConnectorVersion: connector.Version, ConnectorTypeChannelId: channelVersion, Status: dbapi.ConnectorDeploymentStatus{}, diff --git a/internal/connector/providers.go b/internal/connector/providers.go index 079c7f037..46fe3a098 100644 --- a/internal/connector/providers.go +++ b/internal/connector/providers.go @@ -49,8 +49,7 @@ func serviceProviders() di.Option { return di.Options( di.Provide(services.NewConnectorsService, di.As(new(services.ConnectorsService))), di.Provide(services.NewConnectorTypesService, di.As(new(services.ConnectorTypesService))), - di.Provide(services.NewConnectorClusterService, di.As(new(services.ConnectorClusterService))), - di.Provide(services.NewConnectorClusterService, di.As(new(auth.AuthAgentService))), + di.Provide(services.NewConnectorClusterService, di.As(new(services.ConnectorClusterService)), di.As(new(auth.AuthAgentService))), di.Provide(services.NewConnectorNamespaceService, di.As(new(services.ConnectorNamespaceService))), di.Provide(handlers.NewConnectorNamespaceHandler), di.Provide(handlers.NewConnectorAdminHandler), diff --git a/internal/connector/test/integration/features/connector-agent-api.feature b/internal/connector/test/integration/features/connector-agent-api.feature index 71e5febaa..ab00d2c77 100644 --- a/internal/connector/test/integration/features/connector-agent-api.feature +++ b/internal/connector/test/integration/features/connector-agent-api.feature @@ -28,6 +28,10 @@ Feature: connector agent API And the ".status.state" selection from the response should match "disconnected" Given I store the ".id" selection from the response as ${connector_cluster_id} + When I GET path "/v1/kafka_connector_namespaces/?search=cluster_id=${connector_cluster_id}" + Then the response code should be 200 + Given I store the ".items[0].id" selection from the response as ${connector_namespace_id} + When I GET path "/v1/kafka_connector_clusters/${connector_cluster_id}/addon_parameters" Then the response code should be 200 And get and store access token using the addon parameter response as ${shard_token} and clientID as ${clientID} @@ -39,8 +43,7 @@ Feature: connector agent API "kind": "Connector", "name": "example 1", "deployment_location": { - "kind": "addon", - "cluster_id": "${connector_cluster_id}" + "namespace_id": "${connector_namespace_id}" }, "channel":"stable", "connector_type_id": "aws-sqs-source-v1alpha1", @@ -209,6 +212,8 @@ Feature: connector agent API ] }, "connector_id": "${connector_id}", + "namespace_id": "${connector_namespace_id}", + "namespace_name": "default-connector-namespace", "connector_resource_version": ${response.object.spec.connector_resource_version}, "connector_type_id": "aws-sqs-source-v1alpha1", "connector_spec": { @@ -305,6 +310,8 @@ Feature: connector agent API ] }, "connector_id": "${connector_id}", + "namespace_id": "${connector_namespace_id}", + "namespace_name": "default-connector-namespace", "connector_resource_version": ${response.items[0].spec.connector_resource_version}, "connector_type_id": "aws-sqs-source-v1alpha1", "connector_spec": { @@ -390,6 +397,8 @@ Feature: connector agent API ] }, "connector_id": "${connector_id}", + "namespace_id": "${connector_namespace_id}", + "namespace_name": "default-connector-namespace", "connector_resource_version": ${response.spec.connector_resource_version}, "connector_type_id": "aws-sqs-source-v1alpha1", "connector_spec": { @@ -477,8 +486,7 @@ Feature: connector agent API }, "connector_type_id": "aws-sqs-source-v1alpha1", "deployment_location": { - "kind": "addon", - "cluster_id": "${connector_cluster_id}" + "namespace_id": "${connector_namespace_id}" }, "href": "/api/connector_mgmt/v1/kafka_connectors/${connector_id}", "id": "${connector_id}", @@ -570,6 +578,8 @@ Feature: connector agent API ] }, "connector_id": "${connector_id}", + "namespace_id": "${connector_namespace_id}", + "namespace_name": "default-connector-namespace", "connector_resource_version": ${response.object.spec.connector_resource_version}, "connector_type_id": "aws-sqs-source-v1alpha1", "connector_spec": { @@ -680,6 +690,7 @@ Feature: connector agent API "items": [{ "connector_id": "${connector_id}", + "namespace": "default-connector-namespace", "connector_type_id": "aws-sqs-source-v1alpha1", "channel": "stable", "shard_metadata": { @@ -785,6 +796,7 @@ Feature: connector agent API "items": [{ "connector_id": "${connector_id}", + "namespace": "default-connector-namespace", "connector_type_id": "aws-sqs-source-v1alpha1", "channel": "stable", "operator": { @@ -873,7 +885,7 @@ Feature: connector agent API And the ".status.state" selection from the response should match "assigning" And the ".deployment_location" selection from the response should match json: """ - {"kind": "addon"} + {} """ @@ -891,6 +903,10 @@ Feature: connector agent API And the ".status.state" selection from the response should match "disconnected" Given I store the ".id" selection from the response as ${connector_cluster_id} + When I GET path "/v1/kafka_connector_namespaces/?search=cluster_id=${connector_cluster_id}" + Then the response code should be 200 + Given I store the ".items[0].id" selection from the response as ${connector_namespace_id} + When I GET path "/v1/kafka_connector_clusters/${connector_cluster_id}/addon_parameters" Then the response code should be 200 And get and store access token using the addon parameter response as ${shard_token} and clientID as ${clientID} @@ -929,8 +945,7 @@ Feature: connector agent API "kind": "Connector", "name": "example 1", "deployment_location": { - "kind": "addon", - "cluster_id": "${connector_cluster_id}" + "namespace_id": "${connector_namespace_id}" }, "channel":"stable", "connector_type_id": "log_sink_0.1", diff --git a/internal/connector/test/integration/features/connector-api.feature b/internal/connector/test/integration/features/connector-api.feature index 7c522b38d..dc59d1713 100644 --- a/internal/connector/test/integration/features/connector-api.feature +++ b/internal/connector/test/integration/features/connector-api.feature @@ -1103,8 +1103,6 @@ Feature: create a connector "kind": "Connector", "name": "example 1", "deployment_location": { - "kind": "addon", - "cluster_id": "default" }, "kafka": { "id":"mykafka", @@ -1136,7 +1134,7 @@ Feature: create a connector } """ - Scenario: Greg tries to create a connector with an invalid deployment_location.kind + Scenario: Greg tries to create a connector with an invalid namespace_id Given I am logged in as "Greg" When I POST path "/v1/kafka_connectors?async=true" with json body: """ @@ -1144,8 +1142,7 @@ Feature: create a connector "kind": "Connector", "name": "example 1", "deployment_location": { - "kind": "WRONG", - "cluster_id": "default" + "namespace_id": "default" }, "connector_type_id": "aws-sqs-source-v1alpha1", "kafka": { @@ -1160,7 +1157,8 @@ Feature: create a connector "aws_queue_name_or_arn": "test", "aws_access_key": "test", "aws_secret_key": "test", - "aws_region": "east" + "aws_region": "east", + "kafka_topic": "test" } } """ @@ -1168,12 +1166,12 @@ Feature: create a connector And the response should match json: """ { - "code": "CONNECTOR-MGMT-33", - "href": "/api/connector_mgmt/v1/errors/33", - "id": "33", + "code": "CONNECTOR-MGMT-21", + "href": "/api/connector_mgmt/v1/errors/21", + "id": "21", "kind": "Error", "operation_id": "${response.operation_id}", - "reason": "deployment_location.kind is not valid. Must be one of: addon" + "reason": "deployment_location.namespace_id is not valid: KAFKAS-MGMT-9: failed to get connector namespace: record not found" } """ @@ -1186,8 +1184,6 @@ Feature: create a connector "kind": "Connector", "name": "example 1", "deployment_location": { - "kind": "addon", - "cluster_id": "default" }, "connector_type_id": "aws-sqs-source-v1alpha1", "kafka": { @@ -1244,8 +1240,6 @@ Feature: create a connector }, "connector_type_id": "aws-sqs-source-v1alpha1", "deployment_location": { - "cluster_id": "default", - "kind": "addon" }, "href": "/api/connector_mgmt/v1/kafka_connectors/${connector_id}", "id": "${connector_id}", @@ -1296,8 +1290,6 @@ Feature: create a connector "client_id": "myclient" }, "deployment_location": { - "kind": "addon", - "cluster_id": "default" }, "connector_type_id": "aws-sqs-source-v1alpha1", "channel": "stable", @@ -1800,8 +1792,6 @@ Feature: create a connector "kind": "Connector", "name": "example 1", "deployment_location": { - "kind": "addon", - "cluster_id": "default" }, "connector_type_id": "aws-sqs-source-v1alpha1", "kafka": { @@ -1850,8 +1840,6 @@ Feature: create a connector "connector": {}, "connector_type_id": "foo", "deployment_location": { - "cluster_id": "default", - "kind": "addon" }, "href": "/api/connector_mgmt/v1/kafka_connectors/${connector_id}", "id": "${connector_id}", @@ -1900,8 +1888,6 @@ Feature: create a connector "client_id": "myclient" }, "deployment_location": { - "kind": "addon", - "cluster_id": "default" }, "connector": {}, "connector_type_id": "foo", diff --git a/internal/connector/test/integration/features/connector-multitenancy-api.feature b/internal/connector/test/integration/features/connector-multitenancy-api.feature index 94a84d333..978d3bbec 100644 --- a/internal/connector/test/integration/features/connector-multitenancy-api.feature +++ b/internal/connector/test/integration/features/connector-multitenancy-api.feature @@ -10,31 +10,31 @@ Feature: connector namespaces API Given a user named "Gru" in organization "13640210" # eval users used in public API - Given a user named "Stuart" in organization "13640210" + Given a user named "Stuart" in organization "13640221" Given I store userid for "Stuart" as ${stuart_user_id} - Given a user named "Kevin" in organization "13640211" + Given a user named "Kevin" in organization "13640222" Given I store userid for "Kevin" as ${kevin_user_id} - Given a user named "Bob" in organization "13640212" - Given I store userid for "Bob" as ${bob_user_id} + Given a user named "Carl" in organization "13640223" + Given I store userid for "Carl" as ${carl_user_id} # eval users used in admin API - Given a user named "Dave" in organization "13640213" + Given a user named "Dave" in organization "13640224" Given I store userid for "Dave" as ${dave_user_id} - Given a user named "Phil" in organization "13640214" + Given a user named "Phil" in organization "13640225" Given I store userid for "Phil" as ${phil_user_id} - Given a user named "Tim" in organization "13640215" + Given a user named "Tim" in organization "13640226" Given I store userid for "Tim" as ${tim_user_id} - # users in organisation 13640220 - Given a user named "Dusty" in organization "13640220" + # users in organization 13640230 + Given a user named "Dusty" in organization "13640230" Given I store userid for "Dusty" as ${dusty_user_id} - Given a user named "Lucky" in organization "13640220" + Given a user named "Lucky" in organization "13640230" Given I store userid for "Lucky" as ${lucky_user_id} - Given a user named "Ned" in organization "13640220" + Given a user named "Ned" in organization "13640230" Given I store userid for "Ned" as ${ned_user_id} - # users in organisation 13640221 - Given a user named "El Guapo" in organization "13640221" + # users in organization 13640231 + Given a user named "El Guapo" in organization "13640231" Given I store userid for "El Guapo" as ${guapo_user_id} Scenario Outline: Create eval namespace @@ -138,9 +138,9 @@ Feature: connector namespaces API | user | user_id | | Stuart | stuart_user_id | | Kevin | kevin_user_id | - | Bob | bob_user_id | + | Carl | carl_user_id | - Scenario: Create namespaces in cluster for organization 13640220 + Scenario: Create namespaces in cluster for organization 13640230 Given I am logged in as "Dusty" When I POST path "/v1/kafka_connector_clusters" with json body: """ @@ -157,18 +157,40 @@ Feature: connector namespaces API And get and store access token using the addon parameter response as ${shard_token} and clientID as ${clientID} And I remember keycloak client for cleanup with clientID: ${clientID} - # There should be no namespaces at first for organization 13640220 + # There should be default namespace at first for organization 13640230 Given I am logged in as "Lucky" When I GET path "/v1/kafka_connector_namespaces" Then the response code should be 200 And the response should match json: """ { - "items": [], + "items": [ + { + "cluster_id": "${connector_cluster_id}", + "created_at": "${response.items[0].created_at}", + "href": "${response.items[0].href}", + "id": "${response.items[0].id}", + "kind": "ConnectorNamespace", + "modified_at": "${response.items[0].modified_at}", + "name": "default-connector-namespace", + "owner": "${dusty_user_id}", + "tenant": { + "kind": "organisation", + "organisation_id": "13640230" + }, + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + "version": ${response.items[0].version} + } + ], "kind": "ConnectorNamespaceList", "page": 1, - "size": 0, - "total": 0 + "size": 1, + "total": 1 } """ @@ -208,7 +230,7 @@ Feature: connector namespaces API ], "tenant": { "kind": "organisation", - "organisation_id": "13640220" + "organisation_id": "13640230" } } """ @@ -216,38 +238,59 @@ Feature: connector namespaces API # All organization members MUST be able to see the org tenant namespace Given I am logged in as "Ned" - When I GET path "/v1/kafka_connector_namespaces/" + When I GET path "/v1/kafka_connector_namespaces/?orderBy=name" Then the response code should be 200 And the response should match json: """ { "items": [ - { - "id": "${namespace_id}", - "kind": "ConnectorNamespace", - "href": "/api/connector_mgmt/v1/kafka_connector_namespaces/${namespace_id}", - "name": "shared_namespace", - "owner": "${lucky_user_id}", - "version": ${response.items[0].version}, - "cluster_id": "${connector_cluster_id}", - "created_at": "${response.items[0].created_at}", - "modified_at": "${response.items[0].modified_at}", - "annotations": [ - { - "name": "connector_mgmt.api.openshift.com/profile", - "value": "default-profile" - } - ], - "tenant": { - "kind": "organisation", - "organisation_id": "13640220" - } - } + { + "cluster_id": "${connector_cluster_id}", + "created_at": "${response.items[0].created_at}", + "href": "${response.items[0].href}", + "id": "${response.items[0].id}", + "kind": "ConnectorNamespace", + "modified_at": "${response.items[0].modified_at}", + "name": "default-connector-namespace", + "owner": "${dusty_user_id}", + "tenant": { + "kind": "organisation", + "organisation_id": "13640230" + }, + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + "version": ${response.items[0].version} + }, + { + "id": "${namespace_id}", + "kind": "ConnectorNamespace", + "href": "/api/connector_mgmt/v1/kafka_connector_namespaces/${namespace_id}", + "name": "shared_namespace", + "owner": "${lucky_user_id}", + "version": ${response.items[1].version}, + "cluster_id": "${connector_cluster_id}", + "created_at": "${response.items[1].created_at}", + "modified_at": "${response.items[1].modified_at}", + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + "tenant": { + "kind": "organisation", + "organisation_id": "13640230" + } + } ], "kind": "ConnectorNamespaceList", "page": 1, - "size": 1, - "total": 1 + "size": 2, + "total": 2 } """ @@ -260,11 +303,33 @@ Feature: connector namespaces API And the response should match json: """ { - "items": [], + "items": [ + { + "cluster_id": "${connector_cluster_id}", + "created_at": "${response.items[0].created_at}", + "href": "${response.items[0].href}", + "id": "${response.items[0].id}", + "kind": "ConnectorNamespace", + "modified_at": "${response.items[0].modified_at}", + "name": "default-connector-namespace", + "owner": "${dusty_user_id}", + "tenant": { + "kind": "organisation", + "organisation_id": "13640230" + }, + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + "version": ${response.items[0].version} + } + ], "kind": "ConnectorNamespaceList", "page": 1, - "size": 0, - "total": 0 + "size": 1, + "total": 1 } """ @@ -299,18 +364,40 @@ Feature: connector namespaces API # In this part of the Scenario we create connector namespaces #----------------------------------------------------------------------------------------------------------------- - # There should be no namespaces first + # There should be default namespace first Given I am logged in as "Ricky Bobby" When I GET path "/v1/admin/kafka_connector_namespaces/?search=cluster_id=${connector_cluster_id}" Then the response code should be 200 And the response should match json: """ { - "items": [], + "items": [ + { + "cluster_id": "${connector_cluster_id}", + "created_at": "${response.items[0].created_at}", + "href": "${response.items[0].href}", + "id": "${response.items[0].id}", + "kind": "ConnectorNamespace", + "modified_at": "${response.items[0].modified_at}", + "name": "default-connector-namespace", + "owner": "${}", + "tenant": { + "kind": "organisation", + "organisation_id": "${response.items[0].tenant.organisation_id}" + }, + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + "version": ${response.items[0].version} + } + ], "kind": "ConnectorNamespaceList", "page": 1, - "size": 0, - "total": 0 + "size": 1, + "total": 1 } """ @@ -369,11 +456,33 @@ Feature: connector namespaces API And the response should match json: """ { - "items": [], + "items": [ + { + "cluster_id": "${connector_cluster_id}", + "created_at": "${response.items[0].created_at}", + "href": "${response.items[0].href}", + "id": "${response.items[0].id}", + "kind": "ConnectorNamespace", + "modified_at": "${response.items[0].modified_at}", + "name": "default-connector-namespace", + "owner": "${}", + "tenant": { + "kind": "organisation", + "organisation_id": "${response.items[0].tenant.organisation_id}" + }, + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + "version": ${response.items[0].version} + } + ], "kind": "ConnectorNamespaceList", "page": 1, - "size": 0, - "total": 0 + "size": 1, + "total": 1 } """ @@ -389,7 +498,7 @@ Feature: connector namespaces API | Phil | phil_user_id | | Tim | tim_user_id | - Scenario: Use Admin API to create namespace for organization 13640221. + Scenario: Use Admin API to create namespace for organization 13640231. Given I am logged in as "El Guapo" #----------------------------------------------------------------------------------- @@ -414,18 +523,40 @@ Feature: connector namespaces API # In this part of the Scenario we create connector namespace #----------------------------------------------------------------------------------------------------------------- - # There should be no namespaces first in cluster ${connector_cluster_id} + # There should be default namespace in cluster ${connector_cluster_id} Given I am logged in as "Ricky Bobby" When I GET path "/v1/admin/kafka_connector_namespaces/?search=cluster_id=${connector_cluster_id}" Then the response code should be 200 And the response should match json: """ { - "items": [], + "items": [ + { + "cluster_id": "${connector_cluster_id}", + "created_at": "${response.items[0].created_at}", + "href": "${response.items[0].href}", + "id": "${response.items[0].id}", + "kind": "ConnectorNamespace", + "modified_at": "${response.items[0].modified_at}", + "name": "default-connector-namespace", + "owner": "${guapo_user_id}", + "tenant": { + "kind": "organisation", + "organisation_id": "13640231" + }, + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + "version": ${response.items[0].version} + } + ], "kind": "ConnectorNamespaceList", "page": 1, - "size": 0, - "total": 0 + "size": 1, + "total": 1 } """ @@ -444,7 +575,7 @@ Feature: connector namespaces API ], "tenant": { "kind": "organisation", - "organisation_id": "13640221" + "organisation_id": "13640231" } } """ @@ -469,7 +600,7 @@ Feature: connector namespaces API ], "tenant": { "kind": "organisation", - "organisation_id": "13640221" + "organisation_id": "13640231" } } """ @@ -484,11 +615,33 @@ Feature: connector namespaces API And the response should match json: """ { - "items": [], + "items": [ + { + "cluster_id": "${connector_cluster_id}", + "created_at": "${response.items[0].created_at}", + "href": "${response.items[0].href}", + "id": "${response.items[0].id}", + "kind": "ConnectorNamespace", + "modified_at": "${response.items[0].modified_at}", + "name": "default-connector-namespace", + "owner": "${guapo_user_id}", + "tenant": { + "kind": "organisation", + "organisation_id": "13640231" + }, + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + "version": ${response.items[0].version} + } + ], "kind": "ConnectorNamespaceList", "page": 1, - "size": 0, - "total": 0 + "size": 1, + "total": 1 } """ diff --git a/internal/connector/test/integration/features/connector-old-path.feature b/internal/connector/test/integration/features/connector-old-path.feature index f15839cac..d7127c333 100644 --- a/internal/connector/test/integration/features/connector-old-path.feature +++ b/internal/connector/test/integration/features/connector-old-path.feature @@ -22,8 +22,6 @@ Feature: the old connectors path are still valid "kafka_id":"mykafka" }, "deployment_location": { - "kind": "addon", - "cluster_id": "default" }, "kafka": { "bootstrap_server": "kafka.hostname", diff --git a/openapi/connector_mgmt-private-admin.yaml b/openapi/connector_mgmt-private-admin.yaml index a857b7c75..d645f7beb 100644 --- a/openapi/connector_mgmt-private-admin.yaml +++ b/openapi/connector_mgmt-private-admin.yaml @@ -424,6 +424,8 @@ components: properties: connector_id: type: string + namespace: + type: string connector_type_id: type: string channel: @@ -454,6 +456,8 @@ components: properties: connector_id: type: string + namespace: + type: string connector_type_id: type: string channel: diff --git a/openapi/connector_mgmt-private.yaml b/openapi/connector_mgmt-private.yaml index f4b18dd45..7ca854986 100644 --- a/openapi/connector_mgmt-private.yaml +++ b/openapi/connector_mgmt-private.yaml @@ -313,6 +313,10 @@ components: format: int64 connector_type_id: type: string + namespace_id: + type: string + namespace_name: + type: string connector_spec: type: object allow_upgrade: diff --git a/openapi/connector_mgmt.yaml b/openapi/connector_mgmt.yaml index fea7ef3e3..ef3c30e30 100644 --- a/openapi/connector_mgmt.yaml +++ b/openapi/connector_mgmt.yaml @@ -1083,34 +1083,8 @@ components: type: string DeploymentLocation: - discriminator: - propertyName: kind - mapping: - ConnectorCluster: "#/components/schemas/ConnectorClusterTarget" - ConnectorNamespace: "#/components/schemas/ConnectorNamespaceTarget" - oneOf: - - $ref: "#/components/schemas/ConnectorClusterTarget" - - $ref: "#/components/schemas/ConnectorNamespaceTarget" - - ConnectorClusterTarget: - description: "Targets workloads to an addon cluster" type: object - required: - - kind properties: - kind: - type: string - cluster_id: - type: string - - ConnectorNamespaceTarget: - description: "Targets workloads to an addon cluster namespace" - type: object - required: - - kind - properties: - kind: - type: string namespace_id: type: string @@ -1713,8 +1687,7 @@ components: value: name: MyLogger deployment_location: - kind: "ConnectoCluster" - cluster_id: "9bsv0s7tne7g02gh5g4g" + namespace_id: "9bsv0s7tne7g02gh5g4g" kafka: id: "9bsv0s6brfr002pfnkh0" client_id: "srvc-acct-162ef2d8-0209-4117-8462-df63c2025c26" diff --git a/test/cucumber/http_response.go b/test/cucumber/http_response.go index 8443d74d9..41e2d5555 100644 --- a/test/cucumber/http_response.go +++ b/test/cucumber/http_response.go @@ -31,7 +31,7 @@ // Then the ".deployment_location" selection from the response should match json: // """ // { -// "kind": "addon", +// "namespace_id": "default" // } // """ package cucumber From dea66898bb2d67af93d88ec995df5ee2dcf3a3c0 Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Mon, 28 Feb 2022 13:07:11 -0800 Subject: [PATCH 03/12] feat(connectors): added /namespaces endpoint to list cluster namespaces, added required cluster_id to namespace create request, and support for creating user and org kind namespaces --- .../api/admin/private/api/openapi.yaml | 152 ++++++++++++++++-- .../private/api_connector_clusters_admin.go | 131 +++++++++++++++ .../model_connector_namespace_request.go | 17 -- ...connector_namespace_with_tenant_request.go | 4 +- .../internal/api/public/api/openapi.yaml | 147 ++++++++++++++++- .../api/public/api_connector_clusters.go | 132 +++++++++++++++ .../model_connector_namespace_request.go | 4 +- .../connector/internal/generated/bindata.go | 4 +- .../internal/handlers/connector_admin.go | 41 ++++- .../internal/handlers/connector_cluster.go | 33 ++++ .../internal/handlers/connector_namespace.go | 42 ++--- .../presenters/connector_namespace.go | 47 +++++- .../connector/internal/routes/route_loader.go | 2 + .../internal/services/connector_namespaces.go | 84 +++------- .../features/connector-agent-api.feature | 2 +- .../connector-multitenancy-api.feature | 3 +- openapi/connector_mgmt-private-admin.yaml | 62 ++++++- openapi/connector_mgmt.yaml | 64 +++++++- 18 files changed, 832 insertions(+), 139 deletions(-) delete mode 100644 internal/connector/internal/api/admin/private/model_connector_namespace_request.go diff --git a/internal/connector/internal/api/admin/private/api/openapi.yaml b/internal/connector/internal/api/admin/private/api/openapi.yaml index c7fe5df6e..6d7156719 100644 --- a/internal/connector/internal/api/admin/private/api/openapi.yaml +++ b/internal/connector/internal/api/admin/private/api/openapi.yaml @@ -133,6 +133,139 @@ paths: summary: Returns a list of connector clusters tags: - Connector Clusters Admin + /api/connector_mgmt/v1/admin/kafka_connector_clusters/{connector_cluster_id}/namespaces: + get: + operationId: getClusterNamespaces + parameters: + - description: The id of the connector cluster + explode: false + in: path + name: connector_cluster_id + required: true + schema: + type: string + style: simple + - description: Page index + examples: + page: + value: "1" + in: query + name: page + required: false + schema: + type: string + - description: Number of items in each page + examples: + size: + value: "100" + in: query + name: size + required: false + schema: + type: string + - description: |- + Specifies the order by criteria. The syntax of this parameter is + similar to the syntax of the `order by` clause of an SQL statement. + Each query can be ordered by any of the `ConnectorType` fields. + For example, to return all Connector types ordered by their name, use the following syntax: + + ```sql + name asc + ``` + + To return all Connector types ordered by their name _and_ version, use the following syntax: + + ```sql + name asc, version asc + ``` + + If the parameter isn't provided, or if the value is empty, then + the results are ordered by name. + examples: + orderBy: + value: name asc + explode: true + in: query + name: orderBy + required: false + schema: + type: string + style: form + - description: | + Search criteria. + + The syntax of this parameter is similar to the syntax of the `where` clause of a + SQL statement. Allowed fields in the search are `name`, `description`, `version`, `label`, and `channel`. + Allowed operators are `<>`, `=`, or `LIKE`. + Allowed conjunctive operators are `AND` and `OR`. However, you can use a maximum of 10 conjunctions in a search query. + + Examples: + + To return a Connector Type with the name `aws-sqs-source` and the channel `stable`, use the following syntax: + + ``` + name = aws-sqs-source and channel = stable + ```[p-] + + To return a Kafka instance with a name that starts with `aws`, use the following syntax: + + ``` + name like aws%25 + ``` + + If the parameter isn't provided, or if the value is empty, then all the Connector Type + that the user has permission to see are returned. + + Note. If the query is invalid, an error is returned. + examples: + search: + value: name = aws-sqs-source and channel = stable + explode: true + in: query + name: search + required: false + schema: + type: string + style: form + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ConnectorNamespaceList' + description: Connector namespaces + "401": + content: + application/json: + examples: + "401Example": + $ref: '#/components/examples/401Example' + schema: + $ref: '#/components/schemas/Error' + description: Auth token is invalid + "404": + content: + application/json: + examples: + "404Example": + $ref: '#/components/examples/404Example' + schema: + $ref: '#/components/schemas/Error' + description: No matching connector namespace exists + "500": + content: + application/json: + examples: + "500Example": + $ref: '#/components/examples/500Example' + schema: + $ref: '#/components/schemas/Error' + description: Unexpected error occurred + security: + - Bearer: [] + summary: Get a list of available connector namespaces in cluster + tags: + - Connector Clusters Admin /api/connector_mgmt/v1/admin/kafka_connector_clusters/{connector_cluster_id}/upgrades/type: get: operationId: getConnectorUpgradesByType @@ -721,8 +854,12 @@ components: type: object ConnectorNamespaceWithTenantRequest: allOf: - - $ref: '#/components/schemas/ConnectorNamespaceRequest' + - $ref: '#/components/schemas/ConnectorNamespaceEvalRequest' - $ref: '#/components/schemas/ConnectorNamespaceWithTenantRequest_allOf' + required: + - cluster_id + - name + - tenant ConnectorClusterList: allOf: - $ref: '#/components/schemas/List' @@ -841,11 +978,6 @@ components: organisation_id: type: string type: object - ConnectorNamespaceRequest: - allOf: - - $ref: '#/components/schemas/ConnectorNamespaceEvalRequest' - - $ref: '#/components/schemas/ConnectorNamespaceRequest_allOf' - description: A connector namespace create request ConnectorNamespaceEvalRequest: allOf: - $ref: '#/components/schemas/ConnectorNamespaceRequestMeta' @@ -884,6 +1016,8 @@ components: type: string ConnectorNamespaceWithTenantRequest_allOf: properties: + cluster_id: + type: string tenant: $ref: '#/components/schemas/ConnectorNamespaceTenant' ConnectorClusterList_allOf: @@ -940,12 +1074,6 @@ components: required: - name - value - ConnectorNamespaceRequest_allOf: - properties: - cluster_id: - type: string - required: - - name securitySchemes: Bearer: bearerFormat: JWT diff --git a/internal/connector/internal/api/admin/private/api_connector_clusters_admin.go b/internal/connector/internal/api/admin/private/api_connector_clusters_admin.go index 01007ac72..91ec0eecf 100644 --- a/internal/connector/internal/api/admin/private/api_connector_clusters_admin.go +++ b/internal/connector/internal/api/admin/private/api_connector_clusters_admin.go @@ -132,6 +132,137 @@ func (a *ConnectorClustersAdminApiService) DeleteConnectorNamespace(ctx _context return localVarReturnValue, localVarHTTPResponse, nil } +// GetClusterNamespacesOpts Optional parameters for the method 'GetClusterNamespaces' +type GetClusterNamespacesOpts struct { + Page optional.String + Size optional.String + OrderBy optional.String + Search optional.String +} + +/* +GetClusterNamespaces Get a list of available connector namespaces in cluster + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param connectorClusterId The id of the connector cluster + * @param optional nil or *GetClusterNamespacesOpts - Optional Parameters: + * @param "Page" (optional.String) - Page index + * @param "Size" (optional.String) - Number of items in each page + * @param "OrderBy" (optional.String) - Specifies the order by criteria. The syntax of this parameter is similar to the syntax of the `order by` clause of an SQL statement. Each query can be ordered by any of the `ConnectorType` fields. For example, to return all Connector types ordered by their name, use the following syntax: ```sql name asc ``` To return all Connector types ordered by their name _and_ version, use the following syntax: ```sql name asc, version asc ``` If the parameter isn't provided, or if the value is empty, then the results are ordered by name. + * @param "Search" (optional.String) - Search criteria. The syntax of this parameter is similar to the syntax of the `where` clause of a SQL statement. Allowed fields in the search are `name`, `description`, `version`, `label`, and `channel`. Allowed operators are `<>`, `=`, or `LIKE`. Allowed conjunctive operators are `AND` and `OR`. However, you can use a maximum of 10 conjunctions in a search query. Examples: To return a Connector Type with the name `aws-sqs-source` and the channel `stable`, use the following syntax: ``` name = aws-sqs-source and channel = stable ```[p-] To return a Kafka instance with a name that starts with `aws`, use the following syntax: ``` name like aws%25 ``` If the parameter isn't provided, or if the value is empty, then all the Connector Type that the user has permission to see are returned. Note. If the query is invalid, an error is returned. +@return ConnectorNamespaceList +*/ +func (a *ConnectorClustersAdminApiService) GetClusterNamespaces(ctx _context.Context, connectorClusterId string, localVarOptionals *GetClusterNamespacesOpts) (ConnectorNamespaceList, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodGet + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ConnectorNamespaceList + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/api/connector_mgmt/v1/admin/kafka_connector_clusters/{connector_cluster_id}/namespaces" + localVarPath = strings.Replace(localVarPath, "{"+"connector_cluster_id"+"}", _neturl.QueryEscape(parameterToString(connectorClusterId, "")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + if localVarOptionals != nil && localVarOptionals.Page.IsSet() { + localVarQueryParams.Add("page", parameterToString(localVarOptionals.Page.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.Size.IsSet() { + localVarQueryParams.Add("size", parameterToString(localVarOptionals.Size.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.OrderBy.IsSet() { + localVarQueryParams.Add("orderBy", parameterToString(localVarOptionals.OrderBy.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.Search.IsSet() { + localVarQueryParams.Add("search", parameterToString(localVarOptionals.Search.Value(), "")) + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(r) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + // GetConnectorUpgradesByOperatorOpts Optional parameters for the method 'GetConnectorUpgradesByOperator' type GetConnectorUpgradesByOperatorOpts struct { Page optional.String diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_request.go b/internal/connector/internal/api/admin/private/model_connector_namespace_request.go deleted file mode 100644 index 27357c0a7..000000000 --- a/internal/connector/internal/api/admin/private/model_connector_namespace_request.go +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Connector Service Fleet Manager Admin APIs - * - * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. - * - * API version: 0.0.3 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package private - -// ConnectorNamespaceRequest A connector namespace create request -type ConnectorNamespaceRequest struct { - Name string `json:"name"` - Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` - ClusterId string `json:"cluster_id,omitempty"` -} diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_with_tenant_request.go b/internal/connector/internal/api/admin/private/model_connector_namespace_with_tenant_request.go index a2ec3830d..b7fd4e5a3 100644 --- a/internal/connector/internal/api/admin/private/model_connector_namespace_with_tenant_request.go +++ b/internal/connector/internal/api/admin/private/model_connector_namespace_with_tenant_request.go @@ -13,6 +13,6 @@ package private type ConnectorNamespaceWithTenantRequest struct { Name string `json:"name"` Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` - ClusterId string `json:"cluster_id,omitempty"` - Tenant ConnectorNamespaceTenant `json:"tenant,omitempty"` + ClusterId string `json:"cluster_id"` + Tenant ConnectorNamespaceTenant `json:"tenant"` } diff --git a/internal/connector/internal/api/public/api/openapi.yaml b/internal/connector/internal/api/public/api/openapi.yaml index a245e5189..b4775727e 100644 --- a/internal/connector/internal/api/public/api/openapi.yaml +++ b/internal/connector/internal/api/public/api/openapi.yaml @@ -840,6 +840,144 @@ paths: summary: Get a connector cluster's addon parameters tags: - Connector Clusters + /api/connector_mgmt/v1/kafka_connector_clusters/{connector_cluster_id}/namespaces: + get: + description: Get a connector cluster's namespaces + operationId: getConnectorClusterNamespaces + parameters: + - description: The id of the connector cluster + explode: false + in: path + name: connector_cluster_id + required: true + schema: + type: string + style: simple + - description: Page index + examples: + page: + value: "1" + explode: true + in: query + name: page + required: false + schema: + type: string + style: form + - description: Number of items in each page + examples: + size: + value: "100" + explode: true + in: query + name: size + required: false + schema: + type: string + style: form + - description: |- + Specifies the order by criteria. The syntax of this parameter is + similar to the syntax of the `order by` clause of an SQL statement. + Each query can be ordered by any of the `ConnectorType` fields. + For example, to return all Connector types ordered by their name, use the following syntax: + + ```sql + name asc + ``` + + To return all Connector types ordered by their name _and_ version, use the following syntax: + + ```sql + name asc, version asc + ``` + + If the parameter isn't provided, or if the value is empty, then + the results are ordered by name. + examples: + orderBy: + value: name asc + explode: true + in: query + name: orderBy + required: false + schema: + type: string + style: form + - description: | + Search criteria. + + The syntax of this parameter is similar to the syntax of the `where` clause of a + SQL statement. Allowed fields in the search are `name`, `description`, `version`, `label`, and `channel`. + Allowed operators are `<>`, `=`, or `LIKE`. + Allowed conjunctive operators are `AND` and `OR`. However, you can use a maximum of 10 conjunctions in a search query. + + Examples: + + To return a Connector Type with the name `aws-sqs-source` and the channel `stable`, use the following syntax: + + ``` + name = aws-sqs-source and channel = stable + ```[p-] + + To return a Kafka instance with a name that starts with `aws`, use the following syntax: + + ``` + name like aws%25 + ``` + + If the parameter isn't provided, or if the value is empty, then all the Connector Type + that the user has permission to see are returned. + + Note. If the query is invalid, an error is returned. + examples: + search: + value: name = aws-sqs-source and channel = stable + explode: true + in: query + name: search + required: false + schema: + type: string + style: form + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ConnectorNamespaceList' + description: The namespaces visible to user in the cluster. + "401": + content: + application/json: + examples: + "401Example": + $ref: '#/components/examples/401Example' + schema: + $ref: '#/components/schemas/Error' + description: Auth token is invalid + "404": + content: + application/json: + examples: + "404Example": + $ref: '#/components/examples/404Example' + schema: + $ref: '#/components/schemas/Error' + description: No matching connector cluster type exists + "500": + content: + application/json: + examples: + "500Example": + $ref: '#/components/examples/500Example' + schema: + $ref: '#/components/schemas/Error' + description: Unexpected error occurred + security: + - Bearer: [] + summary: Get a connector cluster's namespaces + tags: + - Connector Clusters /api/connector_mgmt/v1/kafka_connector_namespaces: get: description: Returns a list of connector namespaces @@ -1825,6 +1963,10 @@ components: - $ref: '#/components/schemas/ConnectorNamespaceEvalRequest' - $ref: '#/components/schemas/ConnectorNamespaceRequest_allOf' description: A connector namespace create request + required: + - cluster_id + - kind + - name ConnectorNamespaceEvalRequest: allOf: - $ref: '#/components/schemas/ConnectorNamespaceRequestMeta' @@ -1940,8 +2082,9 @@ components: properties: cluster_id: type: string - required: - - name + kind: + description: One of 'user' or 'organisation' similar to 'ConnectorNamespaceTenant' + type: string ConnectorNamespaceList_allOf: properties: items: diff --git a/internal/connector/internal/api/public/api_connector_clusters.go b/internal/connector/internal/api/public/api_connector_clusters.go index d37cec841..3e44a9eb6 100644 --- a/internal/connector/internal/api/public/api_connector_clusters.go +++ b/internal/connector/internal/api/public/api_connector_clusters.go @@ -466,6 +466,138 @@ func (a *ConnectorClustersApiService) GetConnectorClusterAddonParameters(ctx _co return localVarReturnValue, localVarHTTPResponse, nil } +// GetConnectorClusterNamespacesOpts Optional parameters for the method 'GetConnectorClusterNamespaces' +type GetConnectorClusterNamespacesOpts struct { + Page optional.String + Size optional.String + OrderBy optional.String + Search optional.String +} + +/* +GetConnectorClusterNamespaces Get a connector cluster's namespaces +Get a connector cluster's namespaces + * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + * @param connectorClusterId The id of the connector cluster + * @param optional nil or *GetConnectorClusterNamespacesOpts - Optional Parameters: + * @param "Page" (optional.String) - Page index + * @param "Size" (optional.String) - Number of items in each page + * @param "OrderBy" (optional.String) - Specifies the order by criteria. The syntax of this parameter is similar to the syntax of the `order by` clause of an SQL statement. Each query can be ordered by any of the `ConnectorType` fields. For example, to return all Connector types ordered by their name, use the following syntax: ```sql name asc ``` To return all Connector types ordered by their name _and_ version, use the following syntax: ```sql name asc, version asc ``` If the parameter isn't provided, or if the value is empty, then the results are ordered by name. + * @param "Search" (optional.String) - Search criteria. The syntax of this parameter is similar to the syntax of the `where` clause of a SQL statement. Allowed fields in the search are `name`, `description`, `version`, `label`, and `channel`. Allowed operators are `<>`, `=`, or `LIKE`. Allowed conjunctive operators are `AND` and `OR`. However, you can use a maximum of 10 conjunctions in a search query. Examples: To return a Connector Type with the name `aws-sqs-source` and the channel `stable`, use the following syntax: ``` name = aws-sqs-source and channel = stable ```[p-] To return a Kafka instance with a name that starts with `aws`, use the following syntax: ``` name like aws%25 ``` If the parameter isn't provided, or if the value is empty, then all the Connector Type that the user has permission to see are returned. Note. If the query is invalid, an error is returned. +@return ConnectorNamespaceList +*/ +func (a *ConnectorClustersApiService) GetConnectorClusterNamespaces(ctx _context.Context, connectorClusterId string, localVarOptionals *GetConnectorClusterNamespacesOpts) (ConnectorNamespaceList, *_nethttp.Response, error) { + var ( + localVarHTTPMethod = _nethttp.MethodGet + localVarPostBody interface{} + localVarFormFileName string + localVarFileName string + localVarFileBytes []byte + localVarReturnValue ConnectorNamespaceList + ) + + // create path and map variables + localVarPath := a.client.cfg.BasePath + "/api/connector_mgmt/v1/kafka_connector_clusters/{connector_cluster_id}/namespaces" + localVarPath = strings.Replace(localVarPath, "{"+"connector_cluster_id"+"}", _neturl.QueryEscape(parameterToString(connectorClusterId, "")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := _neturl.Values{} + localVarFormParams := _neturl.Values{} + + if localVarOptionals != nil && localVarOptionals.Page.IsSet() { + localVarQueryParams.Add("page", parameterToString(localVarOptionals.Page.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.Size.IsSet() { + localVarQueryParams.Add("size", parameterToString(localVarOptionals.Size.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.OrderBy.IsSet() { + localVarQueryParams.Add("orderBy", parameterToString(localVarOptionals.OrderBy.Value(), "")) + } + if localVarOptionals != nil && localVarOptionals.Search.IsSet() { + localVarQueryParams.Add("search", parameterToString(localVarOptionals.Search.Value(), "")) + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(r) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 401 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + // ListConnectorClustersOpts Optional parameters for the method 'ListConnectorClusters' type ListConnectorClustersOpts struct { Page optional.String diff --git a/internal/connector/internal/api/public/model_connector_namespace_request.go b/internal/connector/internal/api/public/model_connector_namespace_request.go index f8c0445cf..da11f8e47 100644 --- a/internal/connector/internal/api/public/model_connector_namespace_request.go +++ b/internal/connector/internal/api/public/model_connector_namespace_request.go @@ -13,5 +13,7 @@ package public type ConnectorNamespaceRequest struct { Name string `json:"name"` Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` - ClusterId string `json:"cluster_id,omitempty"` + ClusterId string `json:"cluster_id"` + // One of 'user' or 'organisation' similar to 'ConnectorNamespaceTenant' + Kind string `json:"kind"` } diff --git a/internal/connector/internal/generated/bindata.go b/internal/connector/internal/generated/bindata.go index 32c51fb73..ba85b0e3d 100644 --- a/internal/connector/internal/generated/bindata.go +++ b/internal/connector/internal/generated/bindata.go @@ -78,7 +78,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _connector_mgmtYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x7b\x73\xdb\x38\x92\xf8\xff\xfe\x14\xf8\x29\xbf\x2d\xef\xde\x45\xb2\x24\xcb\x2f\xd5\x65\xab\x9c\xd8\xc9\x78\xc6\x71\x32\xb6\x33\x99\xec\xd6\x96\x0c\x91\x90\x84\x98\x04\x68\x00\x54\xa2\xec\xde\x77\xbf\x02\xc0\x07\x48\x82\x14\x29\x2b\xb6\x33\x23\x56\xed\x4e\x4c\x02\x8d\xee\x46\xa3\xd1\x2f\x40\x34\x40\x04\x06\x78\x08\x76\x3b\xdd\x4e\x17\x3c\x03\x04\x21\x17\x88\x19\xe6\x00\x72\x30\xc1\x8c\x0b\xe0\x61\x82\x80\xa0\x00\x7a\x1e\xfd\x02\x38\xf5\x11\x38\x3b\x39\xe5\xf2\xd5\x2d\xa1\x5f\x74\x6b\xd9\x81\x80\x08\x1c\x70\xa9\x13\xfa\x88\x88\xce\xd6\x33\x70\xec\x79\x00\x11\x37\xa0\x98\x08\x0e\x5c\x34\xc1\x04\xb9\x60\x86\x18\x02\x5f\xb0\xe7\x81\x31\x02\x2e\xe6\x0e\x9d\x23\x06\xc7\x1e\x02\xe3\x85\x1c\x09\x84\x1c\x31\xde\x01\x67\x13\x20\x54\x5b\x39\x40\x84\x1d\x05\xb7\x08\x05\x1a\x93\x04\xf2\xd6\x33\xd0\x0a\x18\x9e\x43\x81\x5a\xcf\x01\x74\x25\x15\xc8\x97\x8d\xc5\x0c\x81\x96\x43\x09\x41\x8e\xa0\x6c\xe4\x4f\x7d\xd1\x8e\x5a\x76\x16\xd0\xf7\x5a\x60\x82\x3d\xb4\x85\xc9\x84\x0e\xb7\x00\x10\x58\x78\x68\x08\x5e\xc5\x1d\xc0\x15\x62\x73\xec\x20\xf0\xda\x43\x48\x80\xb7\x90\xc0\x29\x62\x5b\x00\xcc\x11\xe3\x98\x92\x21\xe8\x76\x7a\x9d\xee\x16\x00\x2e\xe2\x0e\xc3\x81\x50\x2f\x97\xf4\xd7\xf4\x5c\x22\x2e\xc0\xf1\xfb\x33\x89\xa6\xaf\x3e\x80\x04\x51\xde\xd9\xe2\x88\xc9\x41\x24\x56\x6d\x10\x32\x6f\x08\x66\x42\x04\x7c\xb8\xb3\x03\x03\xdc\x91\xcc\xe6\x33\x3c\x11\x1d\x87\xfa\x5b\x00\xe4\x10\x78\x0b\x31\x01\x7f\x0d\x18\x75\x43\x47\xbe\xf9\x1b\xd0\xe0\xec\xc0\xb8\x80\x53\xb4\x0c\xe4\x95\x80\x53\x4c\xa6\x56\x40\xc3\x9d\x1d\x8f\x3a\xd0\x9b\x51\x2e\x86\x87\xdd\x6e\xb7\xd8\x3d\xf9\x9e\xf6\xdc\x29\xb6\x72\x42\xc6\x10\x11\xc0\xa5\x3e\xc4\x64\x4b\xc0\x69\xc4\x00\x02\xfd\xcc\xbc\x5c\x2f\x02\xc4\x8b\xfd\x5b\x2d\x5b\xeb\xda\x0d\xc1\x2b\x2f\xe4\x02\x35\xe8\x10\xcd\xaf\xb5\xfd\x56\x00\xc5\x4c\xe1\xff\x4c\xfe\x0f\x58\xbb\x3d\xdb\xda\x02\xa0\x25\xa7\x61\x27\x2b\xa6\x3b\xf3\x5e\x6b\xa8\xe0\x4e\x91\xd0\xff\x00\x20\x66\x88\x7e\xda\x25\x88\x00\xb9\x16\x19\x94\x88\x9c\xb9\x43\xd9\xff\x37\x2d\xae\x6f\x91\x80\x2e\x14\x30\x6a\xc5\x43\xdf\x87\x6c\x31\x04\x97\x48\x84\x8c\x70\xb5\x5a\x22\xc9\x06\x7e\xb6\x6d\x86\xb8\x1a\xed\x19\xe2\x01\x25\x1c\x19\xe8\xb6\xfa\xdd\x6e\x2b\xfd\x13\x48\x71\x17\x88\x08\xf3\x15\x00\x30\x08\x3c\xec\x28\xe4\x77\x3e\x73\x4a\xb2\x5f\x01\xe0\xce\x0c\xf9\x30\xff\x16\x80\xff\xcf\xd0\x64\x08\xb6\x9f\xed\x38\xd4\x0f\x28\x41\x44\xf0\x1d\xdd\x96\xef\xe4\xc8\xdf\x36\x3a\x67\xe8\xfa\x2d\x4f\x4b\x32\x77\x45\xc9\xab\x9a\xb8\x9d\x5b\x38\xb9\x85\xa3\xf4\xbd\x90\x9d\x76\xfe\x9d\x7d\x31\xc2\xee\xff\x46\xfc\x08\x20\x83\x3e\x12\xd1\x7a\xd7\x73\xab\x45\xad\xd0\x65\xcb\x8a\xf9\xf5\x0c\x01\xec\x02\xaa\x34\x66\xda\x09\xc8\x4e\x5b\xe5\xac\x93\x9f\x87\x80\x0b\x86\xc9\x34\x79\x8d\xc9\x10\x48\xd1\x4d\x5e\x30\x74\x17\x62\x86\xdc\x21\x10\x2c\x44\xf5\x65\x32\x5d\xa4\x00\x70\xe4\x84\x0c\x8b\x85\xd9\xf2\x25\x82\x0c\xb1\x21\xf8\x27\xf8\x57\x89\xdc\x26\xb0\x24\xa8\x97\x8b\xb3\x93\xbc\xe4\xbe\x41\x02\xc0\x1c\xbd\x72\x17\x49\xf8\x94\xe1\xd2\xd2\xd6\x8f\x24\xb5\x2d\xab\xd4\x66\x88\x6f\xe5\xba\xa2\xaf\xd0\x0f\x3c\x13\xd1\xf8\xc9\x74\x3b\xd5\xcd\x8a\xad\xec\x43\xc7\x50\x77\x6c\x40\x5a\x65\xcb\xe6\xba\x20\x72\xc0\x87\xc2\x99\xc9\xed\x42\x8a\xa3\x94\x1f\xa4\x34\x7f\xc4\xd2\x41\xb7\xf7\x38\x2c\x3d\x65\x8c\xb2\xfa\xac\x1c\x74\x7b\xab\x32\x30\xed\x5a\xca\xb6\xe3\x50\xcc\x80\xa0\xb7\x88\x48\x83\x00\x93\x39\xf4\x8c\xe5\xdd\x1a\x74\x07\x3f\x08\x93\x06\xab\x33\x69\xb0\x8c\x49\x17\x34\x95\xa5\x9c\x8c\xa1\xaf\x98\x0b\x9e\x32\x6c\xef\xb1\x16\x6a\x43\x86\xed\x75\xbb\xab\x32\x2c\xed\x5a\xca\xb0\x0f\x04\x7d\x0d\x90\x23\x90\x0b\x90\xc4\x0b\x50\x47\x59\x55\x6e\xe3\xfd\xaa\x89\xf9\xb1\x66\x55\xcf\xcb\x2c\x14\x08\x3c\xcc\x85\xdc\xe7\xb2\xc2\xc0\xab\xcc\x94\x65\x9d\x8a\xbb\xaf\x44\xd9\x36\x11\x69\xcb\x9d\x00\x4e\x8d\x49\x58\xda\x9c\xe3\x6f\x4d\x9a\x53\xe6\x22\xf6\x72\xd1\x64\x00\x04\x99\x33\x6b\x3d\xf9\x8d\xec\x1c\x73\x51\xae\x12\x97\xcc\xd4\x66\xef\xa8\xb7\x77\x6c\x54\xe1\x52\x55\x98\xb3\xeb\x1b\x5a\xf4\xb1\x72\x0c\xa4\xc7\xbb\x4c\x3b\xde\x43\x31\x3a\x0c\x41\x81\x4c\x2c\x81\xa9\x16\x5f\xa9\xcf\x2a\x38\xf2\x25\x5d\x32\x36\x5d\x58\xd9\xd2\xae\x00\xa5\x1f\x70\x17\x22\xb6\x30\xf8\xab\x9d\x12\xc8\x17\xc4\x29\xe3\xfa\x7b\xc4\x26\x94\xf9\xca\xf2\x83\x2a\xfa\x00\x30\x01\x90\xe8\x5e\x33\x46\x09\x0d\x39\xf0\x21\x21\x88\x6d\x55\x4b\x9b\x76\x4f\xc6\x94\x7a\x08\x12\xe3\x8b\xc5\x21\x01\xb1\x95\xf9\x92\xba\x06\x83\x4b\xc2\x32\x86\xa3\x6a\x5d\x1c\xd5\x4b\xc3\xbe\x30\x6a\x69\xc0\x4b\x8d\x64\x76\x85\x94\xad\x8f\xa4\x97\x9e\xbc\xd2\x95\x52\xcf\x92\xcf\x00\x69\x55\x39\x77\x65\xdb\x47\xff\x91\xb7\x8f\x72\x6d\xe8\x38\x28\x10\x28\x63\x3c\xff\x18\x0a\x70\xd0\xed\xaa\x79\xc1\x94\xac\xbe\x5b\xe4\x41\x94\xf2\xe9\x37\xb9\x4b\xa8\x96\x5a\x21\xf2\x54\x23\x1a\x9c\xdb\xec\xaf\x1b\xdf\xac\x96\x6f\x76\x9d\xfa\xf6\xc8\x95\x3a\x83\x86\xcc\x41\xc0\xa5\x88\x93\x6d\xa1\xfd\xb3\x8d\x4d\x92\x13\x2c\x02\xc2\x32\xb3\x44\xef\xf6\x71\xd4\x24\xbb\x49\xd7\xf1\xc2\xee\x61\x67\x48\xb3\xbb\x08\xe7\x0f\xe6\x7d\x3d\x69\xdf\xa8\xa9\x5f\xb4\x71\x89\x36\x2e\xd1\xe3\x44\x87\xf8\xce\xbf\xab\x33\x17\x4b\x16\x23\x76\x5b\x0f\xa1\xd2\xcc\x98\x52\x5e\xa1\xe5\x12\x01\x36\xf5\x65\x6f\xf2\x34\x75\x47\xcd\xc8\xfc\x26\x28\xbf\x31\xfc\xea\x30\x69\x13\x94\x6f\xc4\xb0\x7b\xa9\x5d\xdd\xd4\x43\x02\x7d\x4f\x5d\xa8\x47\x28\x55\x87\x27\xea\xf3\x32\x8d\x58\xda\xca\xae\x14\x9f\xca\x42\xb1\xd0\xb0\x71\x77\xff\xb0\x5a\x4f\x4f\xf0\x3d\x74\x5f\x06\x40\x95\x06\x54\x56\x51\xbc\x8d\x82\x2f\x58\xcc\x00\x0f\x90\x83\x27\x18\xb9\xe0\xec\xe4\x47\xd6\x84\xf7\x63\x62\x1e\xc0\x8a\x5a\x31\x90\x3b\xcc\xf7\x54\x8a\x6a\x80\x52\x9d\xf8\x5e\x7e\x5d\xa6\x12\xcb\x1a\x2d\x8f\x45\x9f\x40\x01\x81\xa0\x1a\x89\x5c\xd1\x8e\x94\xa5\xad\x0a\x31\x31\x85\xc4\x47\x6c\x8a\xda\x0a\xca\x7f\xd7\x8d\x54\xeb\xb0\x3a\x1d\x7f\x46\x8e\x28\x01\x2b\x41\x35\x84\x9a\x73\x58\x7f\xbe\x7a\x77\xa1\xf9\xf3\x1c\x5c\xbe\x7e\x05\xf6\x8f\xba\x7d\xd0\x4e\xea\x0e\x05\xa5\x1e\xef\x60\x24\x26\x1d\xca\xa6\x3b\x33\xe1\x7b\x3b\x6c\xe2\xc8\x56\xab\x61\xbb\xfe\x10\x7d\xd2\xf9\x8f\x10\x22\xdf\x78\x02\x7f\xde\x3d\x71\xe3\x09\xfc\x08\x9e\x80\xa5\xd6\x34\x2a\x47\x6e\x5a\x6d\xea\x44\x55\xcc\x4d\x72\xd4\xd9\xd2\xe7\xea\x2c\x74\x8a\x16\xa8\xbd\xf3\x2e\x49\x59\x03\x27\x03\xb3\x46\xea\x3a\xd7\xe3\x4f\x97\xc2\x8e\xc8\x7f\xbc\x54\x76\x24\x05\x2b\x66\xb4\x75\xe7\xf5\x24\xb6\x2d\xb0\x7e\xc8\xfc\x76\x44\xc8\x26\xcd\xbd\x49\x73\x1b\x9c\xdb\xd8\x38\x35\x98\xb4\x49\x73\x3f\x2d\x33\x67\x85\x34\x77\x66\x43\xaf\x55\x74\x9c\x33\x59\xee\x9b\xf6\xce\x83\xab\x93\xfd\x76\xb2\x7d\x6a\x27\xc0\x73\xfd\x1e\xba\x02\xf9\x69\xa6\xb1\xa2\x09\x68\x5c\x21\x9c\x63\xe6\x46\xbb\x6f\x32\xe2\x0f\x7c\x5e\x22\x96\x40\xf3\x88\x5f\xf4\xae\xe1\x29\xbf\xb4\x97\xdd\x01\x28\x3b\xe8\x97\xf5\x86\x1e\xfe\xac\xdf\xfd\x75\xb1\x99\xaf\xcf\x79\x98\x65\xa7\xfd\x2a\x9c\xc6\xea\xa6\x4f\x5a\xff\xd5\x8c\xe1\xc5\x0e\xe0\x26\x96\xb7\xb1\x73\xeb\x30\x69\xc5\x58\x5e\x2c\x66\x9b\x98\xde\xaa\x79\xac\xf0\x41\xd4\x67\x18\xb8\x96\x18\xdd\xcb\xc5\x99\x9b\xd7\xa2\xa1\x1b\xc0\x6c\x1e\xbf\x4a\x91\x2e\x6d\x5d\x3f\xd7\xa5\x51\x74\x57\xcc\x74\x3d\x48\xf0\xaa\x41\xb4\x28\xab\x32\xb2\x51\xba\x68\xcd\x70\x01\x45\xa8\xee\x47\x89\x48\xdf\xe8\xe5\x8d\x5e\x5e\xb3\x5e\xde\xa8\x64\x1b\xcf\xd6\x52\x70\xb5\x06\xad\x9c\x2b\xbc\x2a\xb1\x6b\x8b\x95\x55\x55\x1a\x79\x69\xeb\x4d\x3d\xd6\x46\x2f\x3e\x11\x26\x3d\x60\x3d\x56\x12\x98\xdd\x94\x62\xad\xb3\x14\x6b\x7d\x51\x90\x1d\xe8\xba\x94\x8c\xd2\x28\xc8\x26\x2c\xb2\x5a\x58\xe4\x58\xf2\xf1\x7d\xc2\xb5\xfc\x6e\x52\x12\xfa\xd8\xe6\x40\x4d\x80\xc1\x6f\xdb\xee\xd2\xb8\xf7\x93\x8a\xa5\x64\x59\x53\x19\x49\x96\x22\x93\x12\x03\xc4\x0c\x0a\xc0\x67\x34\xf4\x5c\x30\x46\x20\xe4\xfa\xb6\x41\x87\x92\x09\x9e\x86\x0c\x29\xc1\xd2\xf7\xf4\x99\x1e\x8c\x66\x0a\x25\x5a\xee\x34\xaf\x3a\x9b\xed\xec\x8f\xba\x9d\x6d\xc2\x2f\x3f\x92\xad\x6f\x29\xa9\xba\x80\x3e\xe2\x01\x74\x9a\xdf\xe1\x47\x92\x9e\x8d\xea\xaa\x32\x03\x82\x25\x95\x55\x49\xe3\x46\xfb\xc5\xb2\xda\x2a\x92\x83\x5a\xa7\xba\x2a\xdf\xa7\x49\x69\x52\xd2\xf7\xf1\x8a\x93\x12\x46\xae\x56\x9e\x94\x74\x5f\x4b\x81\x92\x1d\xda\x0f\x59\xa2\x94\x90\xb2\x29\x52\xda\x14\x29\x19\x9c\xdb\x58\x0f\x35\x98\xb4\x29\x52\x7a\x5a\x86\xc3\x2a\x45\x4a\xd9\x7d\xb1\x96\x0f\x58\xb0\x00\xee\x5b\xa8\x54\x04\x58\xa7\x54\x89\xe4\x7b\xd5\x2e\x56\x2a\xf4\xdc\x5c\x98\x98\x3e\x0f\xb3\xdd\x36\xae\x89\x2a\xcc\xd9\x66\x3b\xd9\x54\x45\x3d\x70\x55\x54\x2a\x83\x66\x44\x30\x79\xdb\xb0\x32\xca\xec\x67\xf7\x40\xca\x82\x80\x79\x5f\xe6\xe1\xc3\x80\xeb\xd8\x02\xcc\x40\x60\xc1\x4f\x2c\x8b\xfd\x55\xba\x7e\xcb\x1a\x3f\x71\x9d\x58\xb3\x4e\x2a\xf5\x46\x37\x95\x52\x1b\x63\xbb\x0e\x93\x56\x0c\xd5\xa5\x82\xb6\x09\xd6\x7d\xcf\x5a\xa9\x75\x28\xd3\x5c\xb5\x54\x02\xb2\x66\xbd\x54\xa5\x5a\xad\xd1\xfe\x87\xac\x99\x2a\x8f\xa9\xad\xa7\x6a\x2a\x81\xbf\xa9\x9b\xda\x68\xe9\x07\xd0\xd2\x1b\x05\x6d\xe3\xda\x7a\x2a\xa7\xd6\xa1\xa3\x73\xb5\x53\xa5\x36\xaf\xa5\x1e\xaa\x52\x3f\xd7\x68\xbf\xa9\xa0\xda\x68\xc8\x27\xc2\xa4\x4d\x05\xd5\x9f\xa8\x82\xca\x88\x98\xa0\x39\xf4\xd6\x9e\x68\x3e\x9d\x43\x2f\x54\xef\xd6\x99\x69\xe6\x33\xca\x04\xf0\xf0\x5c\xd2\x9e\x8c\xb0\x52\x02\xba\x56\xf7\x1f\x35\x17\x2d\xb9\x7f\xcf\x7c\xb4\x04\xb1\xde\x9c\x74\x01\xe2\x26\x2f\xfd\xbd\xb1\xde\xe4\xa5\x1f\x8c\x73\x1b\x13\xa3\x06\x93\x36\x79\xe9\xa7\xe5\x82\xdd\x2f\x2f\xbd\x95\x8e\x2a\x91\x8b\x28\x1c\xea\xeb\x0c\x9f\xe9\xff\x07\xaf\xa8\xef\x53\x12\xbd\x52\xff\x39\xc7\xa9\x91\x91\x28\x7e\xc3\x18\xb8\xc5\xc4\x35\xfe\x0c\xe0\x14\x19\x7f\x72\xfc\xcd\xfc\x53\x50\x01\x3d\xe3\x6f\x2c\x90\x1f\x9b\x25\x96\xfb\x1c\x03\x26\x6d\x15\x81\x4d\x56\xcb\xf1\x96\x26\x68\x24\x16\xc5\x46\x98\x08\x34\x35\x8b\xbe\xf1\xb7\x1a\xad\x14\xce\xe5\xcd\xd4\x07\x25\x26\x71\x1b\xe8\x79\xef\x26\x26\x8b\xaa\x04\xec\x9d\xa2\xf7\x12\x4d\x10\x43\xc4\xc9\x64\xb6\x4b\x2e\xb8\xb4\x31\x05\xa8\x35\xe1\x16\xa4\xce\xca\x1c\xa0\x66\x12\x5a\x56\x48\x69\xf3\xc4\x64\x1c\x61\xb7\xb2\x93\xfa\x96\xa3\x69\xd8\x6c\x82\xf1\xf2\xe9\xad\x25\x03\x33\xc9\xf5\xb2\x46\x06\x9e\x6f\x91\x80\x0d\x51\xa4\x5f\x08\x62\x4b\x11\xd0\xa6\xb5\x3b\x82\x19\x3d\x35\xa1\xcc\x87\x62\x28\xcd\x4e\xd4\x16\xd8\x47\xcb\xc0\xf8\xd4\x55\xee\xd6\xaa\x70\xd4\xfb\xe8\x67\xc0\x23\xbb\x08\x53\x72\x85\x84\xd4\x16\xbc\x6a\x69\x63\x73\x61\x87\xcc\xbb\xdf\xa4\x85\xcc\xb2\x8a\x2c\x38\x1e\x3b\x0e\x0d\x49\xa5\xce\x71\x3c\x8c\x88\x18\x65\xf0\x8b\xde\x71\xe4\x30\x54\x35\x77\x49\xdf\xe5\xf3\x67\x42\xac\x46\xfd\x04\x05\x1e\x5d\xf8\x88\x88\x73\xaa\x77\xa0\x86\x12\x65\x26\xb1\xab\x87\xca\xfd\x66\xf9\x03\x29\x1d\x64\xdb\xd5\xd4\x32\x04\xad\xe3\xf7\x67\x11\x52\xd9\x8d\x12\xcb\x8f\xf3\x5e\xf6\xe5\x4c\xa3\x55\xf2\xc3\xf6\x39\x85\xe6\x79\x5a\x58\x0b\x3b\x6d\x5b\x03\x57\x6e\x32\xcf\x6f\xcf\x4b\x06\x29\xfe\x64\x63\xa1\x7f\x44\x58\xe9\x6f\xf0\x94\xab\xe0\x52\x8c\x35\x5f\x21\x63\x70\x91\xfb\xa2\xf6\xc0\xa2\xb9\x90\x9b\x50\x93\xf6\x46\x53\x9b\xd9\xde\xa3\x25\xc6\xcd\x0d\xfe\x17\xc9\x8e\x72\xc5\x90\xb1\x40\x7e\xa2\x9e\xcb\x63\x0b\x43\x9d\x7f\xd1\x1e\x81\x3e\x10\x23\x21\xc8\x7f\x42\x0d\x13\x9c\x11\x2e\x20\x71\x50\x67\x15\x19\x2d\xd5\x58\xe9\x44\x3c\x8b\xee\x5a\x8f\x22\x52\x8e\x31\x2f\x69\x9b\x12\x91\x7e\x96\x9d\x45\xad\x80\xd4\xd0\x97\x68\x8a\xb9\x60\x8b\x35\xb3\x44\x01\x07\x31\xf0\x07\xe0\x8d\x6e\x0c\x58\x3c\xe2\xba\xb8\x14\xcb\x92\x3a\x52\x95\x91\xa4\xec\x21\x2b\x2b\xb7\x5a\xc7\xf9\xe3\x62\xad\xb5\x5b\x07\x73\xe8\x85\x16\xbb\xce\x54\xa2\xc5\xe3\x60\x65\xd8\xc6\x15\x74\xf9\x43\x6e\x59\xb4\xcd\x75\x9d\x5b\xcf\xf5\x4f\xa5\xb5\xf2\xa6\x78\xf1\xba\xdf\x84\xd5\xf9\xa3\x7f\x57\x02\x8a\x9c\xa1\x95\xe1\x0a\x22\xa1\x6f\x4a\x97\x8b\x79\x24\x9d\xc8\xdc\x44\x19\x82\xee\xc2\x3e\x42\x14\xa0\x32\xad\xa5\xb2\x8d\xac\x9a\xf7\x25\x80\xed\x13\xa0\x97\xa4\x34\x76\xcc\xe2\x9c\x34\x01\x0e\xa0\x8a\xdf\x81\xc0\x83\x04\x19\x67\x12\x75\xa6\xb8\xb5\xca\xe2\xaa\x20\xbc\x65\xa7\xc0\xe4\xc9\x0a\xfb\xb0\x86\xfc\xbd\x90\xbb\x52\x9c\xa8\x9a\x32\x9e\x69\x51\xb2\x16\xcb\x3a\xc7\x00\x0a\xae\x47\x13\x2a\x94\xf4\xe6\x42\x9f\xa6\x47\x55\x5f\x98\xd6\x6d\x0e\x35\xa1\xe2\x3e\xf3\x78\x15\xc9\xab\x95\x28\x53\x3f\x35\x22\x2c\x6b\xb7\x34\xf6\x28\xad\x96\x49\x63\x43\xa6\xd9\x15\x67\x76\x15\x68\xbc\x7d\x35\x83\x84\x20\xaf\x42\xd7\xb9\x68\x02\x43\x4f\xc8\xb7\x70\xec\xa1\x12\x0d\x18\x7d\xcc\x32\xfc\x04\x71\xe9\x6b\x34\xd5\xa6\x5a\x6d\x9a\xb0\x69\x10\x64\x14\xab\x1b\x65\x63\xb3\xc3\x35\x1d\x07\x72\x8e\xa7\xc4\xdc\xeb\xe2\x77\x99\xc1\x94\x6a\xcc\xb6\x5a\x8e\xe1\x04\x62\xaf\x88\x72\x16\x8a\x9b\xcb\x29\xb7\xa5\xe8\xcc\xb1\x34\xfd\xf3\x0d\x33\x1f\x72\x52\x6d\xda\x49\x95\xa1\x25\x69\xdd\x99\x48\x6b\xb3\x67\x04\xb5\x87\x68\x7c\x29\xfc\x70\xb9\x2d\x70\x24\xa1\x99\xe2\x59\x25\x98\x25\x46\x71\xba\x98\x72\xb8\x14\xe1\x6e\x57\x59\x6e\x91\x8f\xbb\x9d\x82\x53\xdf\x47\xb1\xb1\x56\x17\xcd\x65\x16\x6b\x8a\x6f\xc2\x21\x13\xf4\x33\x23\x50\x58\x65\x1e\xca\x96\x6a\x9b\xe5\x33\x18\xa0\xcc\xeb\x80\x51\x07\x71\x6e\xfe\xee\xa8\x7c\xad\x83\x93\x33\x48\x5c\x2f\x1b\x4b\xca\xa8\xa0\xac\x5c\x58\x2c\x0c\x9b\x54\x48\x0b\xc3\x36\xf5\x23\x09\x3a\x1b\x13\x70\x13\xb7\x7c\xe4\x45\x7e\x79\xe6\xab\x5a\xec\x23\xb5\x7d\xad\x6a\xd2\x14\xf8\x1b\xa3\xb1\xbc\x47\x56\x91\x2d\x9b\xea\x48\xef\xa5\x33\x6a\x21\xae\x2e\xac\x62\xb8\xc2\x04\x6b\x70\xa5\x36\x72\x36\x05\x9a\xdf\xcd\x72\x86\xde\x6a\x46\x59\xc6\xe0\x69\xd8\x37\xa3\x78\xf2\xd8\x3d\x86\x11\x57\x42\x4c\xc3\x6d\x3a\x4e\x9e\x8c\xe6\x3a\x0a\x63\xdf\xb1\xf3\x61\x6d\xfd\xc4\x51\x44\x4c\xc4\xfe\xc0\xb2\x3b\x3d\x59\xcb\x71\x0d\x26\xe3\xa3\xd8\x8a\xeb\x10\xdc\x86\xbd\xed\xb6\xe5\x9f\xc0\xa8\x6c\xd9\x8d\x49\x70\x9d\xfc\xca\x78\xde\x9b\x96\x5f\xac\x8e\xe8\xdf\xdb\x09\x0a\x97\x28\x60\x88\xcb\x11\x33\xa5\x82\xaa\xb6\x9f\x87\x41\x40\x99\x40\x2e\x18\x2f\x94\xc3\x7a\xfc\xfe\x2c\xea\x48\x09\xca\xf2\xb8\xb8\xb7\x81\xe2\xfe\xa6\x5f\x45\x0b\x3b\xf7\x56\xd3\xbb\x4e\x88\x9f\x39\x25\xa3\x0c\xd8\x47\xca\x65\xe5\xb7\xdc\xc2\x7c\x5c\x40\x1f\x15\x0f\x74\xc9\x51\x3a\x55\x0a\xc0\xfc\x50\xa2\x2d\xb3\x45\x0f\xba\xcd\x3d\x47\x8a\x76\xfa\x82\x18\x67\x4b\x93\xa2\x46\x4d\xc6\x5a\xd7\x82\xc9\x9b\x16\x79\xe4\xaa\xf0\x3e\x36\xff\x2c\x20\x5f\x9b\x47\xd8\xa1\x64\x94\x4f\xd9\x15\x06\xfb\x70\x79\xae\xa2\xa9\x44\xb5\x5f\x7d\x34\x0f\x8e\x97\xcd\xc7\xb9\x6a\x92\xde\xf7\x04\x05\x9a\x52\x86\xbf\xa1\xec\x90\xf7\x9d\x97\x72\xa1\x81\x01\x1c\x63\x0f\x17\x17\x87\xed\x54\x9b\xd1\xb8\xa8\x84\x1c\x39\xdf\xdf\x15\x59\x7b\x69\x45\x99\x06\x8d\x9f\x63\xa5\x70\xe2\x40\xb5\xba\x68\xcb\x81\xc4\xbc\x65\x6b\xae\x8b\x8e\x10\x80\x05\x33\xb2\x00\x2d\x5d\x30\x13\x8c\x3c\xd7\x2e\x0b\x05\x0d\x04\x4c\xa5\xf7\xe3\x10\x50\xdc\xb6\xfe\x04\xfb\xb9\x24\xb3\x34\x48\x9e\x2b\x73\x7d\x96\xe5\x50\xfe\x8c\x52\x03\x27\xb3\x41\x06\x77\xa9\xcf\x07\x09\xa1\x02\x16\x12\x84\x76\x76\x59\x58\x55\x2a\xc4\x65\xb3\x63\xdf\x4a\x2b\x56\xb2\x25\x7d\xb2\xa4\x87\xdd\xea\xb0\xda\x1d\xca\xf2\x90\xe0\xb7\x4a\x66\xe7\x31\x9c\x30\x9b\x68\xe4\x8d\xe5\xa4\xcd\x35\x22\x30\x8d\xf6\x58\xa6\x63\x49\x89\x94\x8b\xa5\x46\xf1\x31\x81\x99\x50\x4c\x34\x7d\x8b\x0b\x75\xb6\x3d\x53\x52\xe5\xc3\x20\xc0\x64\x6a\x72\x37\xe4\x88\xd5\x27\x4b\xa3\xfc\x81\x67\x7f\x40\x82\xb2\x29\x24\x98\xc3\x28\xcf\xd2\x08\xd6\x3b\xa3\x6f\xab\xcc\xbc\x6d\xc6\x7b\x1b\x92\xab\xc1\xc8\x22\x57\x39\x8d\x72\xb8\x86\x75\x1a\xb5\x2a\x8f\xe4\xfc\x2c\xad\xe3\xa8\x83\xff\xf7\x40\xce\x9c\xf8\x15\x90\xac\xca\xd6\x1d\x5b\x8f\xd3\xe9\x32\xa8\xdc\xb9\xfa\xd5\x82\x3f\xd5\x15\xf3\xcd\x2b\xe6\x92\xcb\x63\x2b\xa3\x06\xe9\xeb\x4a\x07\xab\x84\x61\x06\xaa\x76\xa6\x91\x25\x87\x1b\xbe\x0b\x03\xeb\xe8\xba\x3f\x81\x45\x61\x1c\x0f\x28\x61\x42\x7d\x41\x5f\xb3\xc7\xdc\x0c\xff\x7b\x86\x10\x6d\x46\x42\x53\xc7\xb9\x7e\x98\xd1\xfc\xd2\x78\x0d\x02\x80\xbe\x06\x98\x15\xe2\xdd\xf2\x79\x56\x55\x14\x59\x09\x53\x64\xf6\xf5\xf8\x59\x65\xff\x69\xd5\xd0\x17\x46\x92\x02\x34\x8b\xd0\x58\xee\xbb\xd6\x1f\x34\x01\x52\x8a\xf3\x77\xe6\xa4\x9c\xd5\x37\xe7\x24\x5d\x0b\x5e\xe4\xd9\x89\xf4\xa8\x19\x72\x28\x4b\x2e\x3b\xc8\xf9\x45\x16\x06\xe6\x2e\xc3\xb1\x1c\x0e\x32\xab\xb1\x35\x0e\x46\x95\x78\xfe\xa7\x98\xb3\x3f\xb8\x0c\xa7\x08\x60\xe2\xa2\xaf\x05\xe8\x13\xe8\x71\x54\x1f\xcb\x62\xcd\x7e\xbe\x46\x5c\xdb\xbd\xa0\x15\x55\x21\x9a\xc5\xe1\x1a\x69\xa3\x96\xbd\x12\xe9\x8b\xd0\x1f\x23\x26\x59\xa9\x54\x13\xc0\x04\x20\xe8\xcc\x4c\xa2\xd7\x48\x46\xbe\x88\x3d\x21\xa3\xdb\xd5\x84\x44\x37\xa0\x59\x15\xd9\x7f\x52\x9f\xf6\x2a\x3a\xd6\xa9\x6b\xd9\x54\x27\x30\x5e\x00\x87\x61\x81\x18\x86\x1d\x25\x21\x7c\x41\x04\xfc\xaa\xe3\x2e\x98\xa7\xa2\x06\x30\x37\x10\xf2\xb1\x07\x99\xf4\x7e\x45\xae\x0b\x02\x37\x31\xe0\x1b\xe0\x78\x30\xe4\x2a\x88\x07\x09\xb8\xfa\xf5\x5c\x27\x03\x7c\x44\x44\xea\xf9\x9e\x4a\xbe\x29\x46\xc7\x8e\xb5\xea\xaf\x43\x1b\x90\x2c\x12\xb0\x19\x1f\xf1\x46\x3b\xd0\x3c\x85\xf3\x9a\xb2\x98\x75\xcf\x25\x62\x4c\xdd\x6a\x27\x75\xb5\xe1\x41\x4a\x76\x73\x73\x00\x31\x43\x58\x2b\xf8\xe7\xd2\xa6\x53\x23\x4d\xa8\xe7\xd1\x2f\x98\x4c\x23\xc2\xa2\xa2\x38\xf9\xdc\xdc\xdc\xf0\x3b\x2f\xe3\x10\x02\xc8\x1d\xf3\x7b\xda\xf8\xba\x39\x12\x60\x04\x89\x3b\x8a\x15\xc3\x7d\x50\x7a\x1e\x03\x29\xc7\xef\x4c\x33\xd6\x9c\x61\xb2\x2d\x74\xbe\xdf\x45\xee\x73\x40\x19\xc0\xba\x8d\x92\x38\x80\x39\x40\x7e\x20\x16\xcf\xe5\xbb\x54\x6d\xe9\xaa\x2d\x1e\x7a\x82\x03\xc8\x32\xf3\x27\xb1\xe9\x24\x72\x1d\x78\xd4\x45\x99\x13\x85\x45\x59\xcf\x89\xb2\x29\xee\x31\x69\xad\x92\x15\xaa\x97\x70\x04\xe0\xbe\xab\x90\x8b\x85\x87\x86\x6a\x57\xd3\xba\x42\x5d\x19\x68\x5f\x61\xe9\x02\x53\x8d\xd2\x05\x65\xc8\x42\xf5\xca\x5a\xb2\xa2\xbe\xcc\x10\x43\x99\xe5\x94\x0e\x99\x59\x55\xe0\x58\xca\x09\x72\xa3\xd5\x21\xf5\x92\x02\xa7\xf1\x92\x93\x73\x23\xb9\x74\xf3\x1c\xdc\x18\x24\xc8\x3f\x23\x69\x91\xff\x54\x91\xd3\x9b\xe7\x00\x12\x17\xdc\x44\x81\xed\x9b\x74\xa1\xc5\x43\xe8\x03\x23\x94\xe9\x49\xbf\xf9\x9f\xbf\xcb\xbe\x2f\x6e\x94\xd8\xdc\x9c\x9f\xfd\x72\x6a\xe9\xe3\x50\xf2\x39\x24\x8e\xc0\x73\x94\xef\x7f\x7c\x71\x72\xa3\x87\x7c\x77\x79\xd3\x01\x3f\xd1\x2f\x68\x8e\xd8\x73\xb0\xa0\xa1\x52\x0c\x92\x72\x08\x7c\xf8\x15\xfb\xa1\x2f\x79\xd0\xeb\xa6\xe0\x28\x51\xb4\xc2\x98\x52\x25\x16\x06\xfb\x4f\x13\x39\xb3\xad\xce\x5c\xde\x48\x9f\x81\x97\x7c\x53\x12\x77\x03\xbf\xf0\x36\xbf\xe3\x6d\x9d\x82\xd5\x48\xaa\x98\xab\x66\x0d\xb8\xd1\x75\x46\x37\x75\x97\x6b\x76\xad\xbe\x00\x59\xf8\x0a\x7c\x0c\xfa\x45\xb6\xc0\x49\x75\xff\x67\xd0\xfe\x97\x9d\x0c\x5d\x92\x8d\xa3\xb2\x63\x4d\x06\xd4\xa3\xe8\x9f\x38\x10\x90\x09\xae\xdf\x4b\xaa\x56\xc4\xd8\xc3\xb7\x48\x22\xfd\x97\xfe\xde\x77\x51\x2c\x4a\x5d\xca\x8f\xd9\x69\x31\xf4\x0d\x14\xea\xbb\x74\xc2\xc1\x0c\x72\x10\x20\xe6\x63\xce\xa3\x9a\x6c\x8e\x90\x12\x29\xcd\x17\xe4\x1a\x72\x70\x41\x05\xea\xc4\xf8\xe9\x4d\x27\x3d\xbf\x29\x25\x3e\xaa\x6a\xc1\xdc\xe8\x5d\xae\xbe\x22\xa3\x41\xc9\x5c\x89\x52\xb2\x2b\x20\xcb\x1e\x9f\xd1\x2f\x20\xaf\xf6\x6a\x49\x49\x6b\x35\xf5\xb6\x95\x5e\x01\xa0\x8a\x8d\x62\xb4\xa2\x3b\x00\x4c\xa0\x68\x08\xc6\xea\x6d\xf4\x52\xff\xf1\x3a\x32\xc9\x7f\xfe\x78\xbd\x65\x8e\x38\x13\x22\x90\xd0\xb3\xd4\xe6\x0b\x02\xad\x67\xda\x73\x11\x4a\xcd\xe8\xd6\xdb\x45\xe6\x77\x52\x33\x16\x41\x35\x00\xec\x0e\x81\x47\xa7\x23\x8e\xc9\xed\xa8\xdb\xe9\x25\x1f\xf4\x39\x90\x0c\xa4\xe4\x5b\xa3\x33\x26\xaa\x28\x88\xef\x98\x83\xb4\x72\xf8\x9f\xd3\x29\xb8\xc2\xe4\x36\x79\x1d\xbb\x59\xa0\x95\x69\x6d\x4b\x26\xb6\xf3\x9a\x20\x9b\xc9\xca\x43\x4e\x73\x6d\x2b\xe2\xdf\x09\xc8\x34\xc5\xa8\x98\x4c\x6b\x03\x6e\x8e\x57\x96\xca\x6a\xab\xa2\xb2\x51\xbe\xa8\xac\x6d\x2b\x2a\x2b\x26\x68\xca\x0f\xe1\xf8\x7e\xd1\x35\x4c\x97\x5a\x7a\x6b\x45\xfc\x08\x2c\x3c\x3d\x03\x56\x77\xd1\x12\x6e\xaf\x0a\xb8\x03\xe0\x87\x9e\xc0\x23\x0f\x13\xeb\x29\xe0\xa4\x3c\xd5\x5c\xf3\xd9\x06\xc6\xe4\xbd\x95\xb0\xc0\x39\x26\xb6\x96\x11\xe2\xd5\x6d\x14\x0d\x63\x4a\x3d\x04\x89\xe5\xfb\xd7\xf6\x94\xd1\x30\x18\x82\x16\x22\x6e\x40\x31\x11\xc5\xf3\x50\x7c\x46\xbf\x8c\xa0\xe7\xdd\x9f\x9c\xab\x19\xfd\x22\x37\xfc\x72\x62\xaa\x5a\xdc\x93\x14\x41\x03\xec\x2c\xc9\xc2\x53\xdf\x97\x86\x82\xdc\x9e\x04\x72\x93\xe3\x1f\x7a\xf7\x54\x00\x74\xc4\xc7\x2e\x42\xd7\xe5\x0d\xca\x13\x2e\x29\xda\x6a\xd5\x65\x71\xe6\x02\x05\xf7\x0f\x86\xe5\xa2\xf3\xe9\xd3\xae\x14\xe4\x08\x26\xe1\x88\x89\x91\xb2\x1a\xcb\xda\x94\xfb\x95\xc5\xe7\xd8\x75\x55\xe9\x4c\xc8\x05\xf5\xb5\x31\x1a\x9b\x23\x0e\x55\xf6\x89\x88\xb6\xfe\xc8\xe0\xf5\x11\xe7\x3a\x10\x00\x04\x83\x84\x63\x91\xcf\x8d\xa6\xcf\x72\x72\xe4\xb3\x84\x96\x02\x3d\xd7\xb1\xbd\x17\x19\xdd\x1a\x69\x41\xa5\x47\x0a\x5d\x17\xb9\x95\xa0\x22\xe1\x78\x2d\x3b\x55\x37\x2c\x17\x12\xf3\x29\xc9\xd0\x55\x62\xaf\x19\x6a\xa2\x5f\x07\xe5\xdf\x54\xb2\xee\xde\x28\x97\xa5\x08\xcd\xa7\xbd\x14\xab\x38\x77\xb8\x04\xe7\x33\x25\xae\x9a\xdb\xe0\xd8\x11\xf9\x20\x5a\x11\x7b\xab\x82\xaf\x87\x79\x3b\xb3\x3a\xac\x8d\x96\x8c\x51\x67\x05\xa2\xaf\x82\x41\xa7\xd9\x12\x3c\xd5\x7d\x00\x8c\x84\x75\xc2\xa8\xaf\x26\x7f\x4c\xdd\xbc\xd6\x48\x9f\x3f\xfe\xf2\x59\x87\x2c\x46\x18\xc5\x2c\x7e\x28\x51\xcb\x88\xc1\xf7\x92\xb5\x19\xe4\xa3\x19\x82\x2e\x62\xa3\x09\xf6\x04\x2a\x54\xd4\xa6\x4f\x66\x8e\x5f\xab\xc6\x60\x0c\xb9\x74\xff\x75\x68\x41\x17\x4a\x3a\x6a\xde\x29\x41\x40\xc3\xbd\xa7\xf0\xd9\x0b\x1a\x4a\xf1\x92\xb2\xa7\xc7\x8d\x9c\x5d\x1a\xe7\xdb\xaa\x15\x5b\x7c\xe2\x3d\xea\x7c\x51\xac\x65\xc8\x3e\x91\x4c\xfc\xa4\x87\x5a\xde\x7c\x7d\xb2\x6a\x29\xb3\x28\xa2\x05\x79\x8c\x5a\x34\x51\xdf\x5f\x5c\x0b\x92\x54\x4f\x64\x53\x17\xb0\xb6\xef\xf7\x76\x71\x4e\xa7\x66\xd6\x69\xc9\xd9\x88\xcc\x6d\x0b\xa0\x75\x34\xe6\xf3\x2e\x3f\x10\x04\x1d\x4c\xbb\xfd\xe9\x6c\x6f\x3a\x30\xdc\x9b\xc2\xb9\x21\xa3\xcf\xfe\x98\x4d\x58\xb7\xdb\x0f\x26\xe4\x76\xd6\x35\x2d\xb7\xf4\x9a\x09\xd0\xe2\x6c\xee\xb4\xa1\xe3\x88\x76\x6f\xbf\x8f\x26\x7d\xf7\xb0\xdd\xed\x77\x8f\xda\x83\x5e\xef\xa0\x7d\x38\xd8\xef\xb7\xdd\xc9\xfe\xae\xd3\xef\xf6\xf7\x9c\xfe\xbe\x05\x4a\x74\x05\x05\x68\x8d\x7b\x83\x81\x7b\x74\xd4\x6b\x77\x0f\xd1\xb8\x3d\x18\x1c\xf4\xdb\x87\xc8\xe9\xb5\xd1\xb8\xbb\x3b\x70\xf6\x8f\xfa\xbb\xbd\xb1\xd9\x3f\x64\xde\x10\xb4\x26\x94\xb6\x6d\xf8\x76\x6e\x21\xef\x40\xc7\x47\x1d\x87\xfa\xc3\xc1\x60\xb7\x95\x73\xb7\xac\xe7\x91\x0c\xf2\xbb\xb7\x87\x1e\x99\x76\x77\x7b\x1c\x1d\xdd\xd5\x20\x1f\x75\xfb\x7b\xfd\xfd\x3d\xd4\x86\x87\x87\xb0\x3d\x18\x4c\xc6\xed\xc3\xc1\x5e\xb7\x8d\xdc\x6e\xaf\x8b\xc6\xfb\x63\x67\xcf\xa9\x22\xdf\x75\xf6\xe0\x61\xff\xe8\xb0\x3d\x46\xee\x41\x7b\xd0\xef\xa3\xf6\xe1\xd1\xe0\xa0\x3d\xd9\x9f\xb8\x70\xff\xa8\x7f\xd4\x9f\x4c\x8a\xe4\x8f\x21\x8b\xc8\xef\xfb\x13\x07\x76\xbb\x7d\x71\x74\x77\xc0\xa7\x1d\xce\xca\xc8\x8f\xcf\xe6\xe4\xfd\xea\xe2\x29\x1f\xd0\xb2\x3b\xf5\xd6\xf3\x56\x36\xd7\x34\xf1\xad\xcc\xd8\x91\x7e\x52\x3f\x92\x17\xbe\x46\xbe\x8c\x9a\xdc\xe7\x63\x98\xbd\x07\x36\xf1\xaa\xb3\x43\xdd\xa2\x45\x7e\xb5\xc6\x09\xd3\xd6\xd5\xf5\xe5\xd9\xc5\x9b\xac\xef\x61\xb5\x33\x93\x1e\x3f\x5f\xbd\xbb\xc8\x5d\x8a\x11\x39\xed\x85\x74\x67\xa5\x03\x11\x85\x6f\xd4\x57\xa9\x36\x8b\xee\x67\x1c\xec\x52\x4d\x94\x49\x5a\x76\x5c\x29\x77\xce\x51\xc5\xeb\x46\xf1\x29\x34\x73\x68\x17\x41\x77\xe4\x21\x21\x10\x1b\xdd\x85\x28\x4f\xa6\xe2\xae\x14\x38\xef\x2e\x17\x4d\xaa\xfe\x05\xc0\xb2\xc8\x94\xe5\x1a\x42\x23\xf1\xbd\x4c\x03\x95\x54\x0d\xc6\x3f\x85\xd2\xca\x86\x6f\x3a\x30\xc0\x1d\x1a\x20\xc2\x67\x78\x22\xa4\x6c\xef\x04\x8c\x4e\xb0\x87\x6c\xb3\x0b\x5a\x91\x03\xdf\xce\x34\x6a\x70\xc3\x64\x19\xcd\xb2\x83\x85\xee\x47\x20\xa6\xfc\xa2\x44\x4b\x20\xb0\xd5\xeb\x1a\x8a\x20\xba\x09\x26\x77\x0d\x5c\x75\xec\x4c\x5f\x8f\xb8\x93\x81\xa3\x2e\xe7\x02\xad\x57\xef\x2e\x2e\x4e\x5f\x5d\xbf\xbb\x6c\xbf\x7d\xf3\xf6\xba\x9d\x69\x12\x5d\xc9\x05\x5a\x57\x0b\xe2\xcc\x18\x25\x34\xe4\x00\xaa\x5d\x1a\x60\x0e\x08\x15\x69\x65\xb7\x8e\xcd\x43\xbe\x20\xce\x0b\xa9\x18\x8a\xd7\x69\xe4\xee\xec\x02\xad\x1e\xfe\x78\x86\xfd\xbb\x37\x0e\x3b\x09\xcf\xf7\x7b\xf0\xc3\xd7\xb3\x7f\xdc\xbd\xbc\xbe\xbb\xb8\x84\x09\x97\xce\x74\xac\xfb\xd7\x10\xb1\x45\x0d\x4e\xf5\xd7\xc4\xa9\xfe\x52\x46\xf5\x2d\x7c\xfa\x8f\x21\x00\xaf\xd5\xe1\x65\x69\xdb\x05\x90\x71\x94\xc9\xf4\x0c\xc1\x07\x22\x95\xb8\xfc\xaa\xc2\x39\x3a\x96\x13\x95\x53\x71\x75\xb7\x04\x0c\xf0\x48\x87\x3c\xa3\x73\xbd\x43\x50\xc0\x60\xd8\x60\xbc\xb4\x04\xdf\xa1\x5e\xe8\x13\x6d\x7a\xca\x91\xa2\x50\x3e\xd8\xc6\xee\x76\x07\x5c\xd9\xda\xa9\x9c\x97\x39\x9a\xdc\x85\x29\x79\x1e\x65\xa2\x1d\x8f\x86\xee\x28\xca\x97\xb0\xf8\xad\x3e\x8a\xd7\x01\xbf\xea\xbc\x85\x9e\xc8\x21\xc0\x2e\x78\x01\x7a\xfd\xdd\x52\xa9\xf0\x3e\x9e\xbc\x09\x17\xe3\x33\x76\x4a\xbe\xb2\x63\xe4\x1f\xf4\x07\xd3\xbb\xdb\x5b\x7c\x32\x8f\xa5\x22\x7f\x0b\xa4\x4d\x12\x06\xdd\xc1\x5a\x24\xe1\x60\x99\x20\x1c\x58\xd6\x4b\x9d\xab\x24\x13\x62\xac\xb7\x3e\xdb\x48\x3a\x78\x3c\x82\xd2\xd4\x96\x0a\x8b\x61\xf7\xc5\x76\x0f\xff\xb2\xeb\x86\xbf\x7d\x3a\x9b\xcf\xf7\x3e\xcd\xcf\xbd\xc5\xb7\x9e\xff\xe6\x72\xf7\xe7\xc5\xdd\xc5\xb6\x52\x0d\x13\x1a\x12\xb7\x62\xf1\x7f\x7a\x77\x30\xed\x4f\xf7\x7f\xba\x76\x3f\xfc\xf2\x01\xf6\x6f\xf9\x4f\x87\xfd\xdb\x5f\x4f\x76\x17\x31\x67\xf2\x37\xa2\x5a\x55\x63\x6f\x3d\x9a\xb1\xb7\x54\x31\xf6\x2c\x6c\x49\x97\xf1\x1c\x31\x3c\x59\x80\x9f\x3f\x5e\xeb\x7b\x56\x87\xe0\x32\xf2\x46\x00\x0c\xc5\x8c\x32\xfc\x2d\xbe\x84\xe9\x16\x91\x7a\xfc\xd9\xfd\x30\x3b\x9d\x7d\xf1\x7f\x7f\x19\x7c\x7c\x3f\x39\xeb\x7b\x17\xe8\x36\x70\x07\xff\x38\x89\xf9\x73\x24\xf7\xb2\x57\x94\x4c\x3c\xec\x88\x1a\xbc\xda\xdd\x5f\x0b\xaf\x4c\x30\x76\x5e\x99\x2d\x4c\x11\xd2\x47\x7e\xb4\xe6\xc1\x1c\x40\x4f\xd9\x46\xea\x64\x4a\x29\x1f\xf6\x6f\x3f\x75\x3f\xe0\xd3\xdb\x6f\xb7\xbf\xbf\xfa\xf6\xf1\x3d\x3a\xeb\xd3\x4f\x68\xe6\xee\x9e\x46\x6c\x28\xde\x6f\x6a\x23\xfd\x68\x2d\x94\x1f\x2d\x23\xfc\xc8\x2a\x23\xe9\xf5\xf4\x28\x3b\x68\x61\xca\xd1\xe9\xf9\xfc\xf5\xd1\xe7\xb7\xbf\x7e\xda\xff\x34\x9d\x4d\xde\x1e\x4d\xdf\x5c\xf2\x9f\xe6\xa7\x1f\x13\x5a\x6b\x2b\x8b\xc7\xa3\xd8\xdc\x05\xd5\x98\xc9\xbd\x1d\x40\x5a\x07\x5c\xfa\x4d\xef\x5e\xbd\x6d\x9f\xfe\xde\x3e\x1a\x46\x97\x7c\xc8\x25\xa4\xaf\xf2\x48\xdb\xa0\xaf\xa2\x1d\xed\x7d\x30\xc0\xed\x1e\xfe\xda\xdd\xf5\x88\xeb\xf9\x77\xdd\xbb\x89\x73\xc0\xb1\x80\x7b\xdc\xfb\x3c\x3f\x34\x9d\x90\x89\xf1\x43\xf4\x92\x0f\xbd\xe9\x9e\x7b\x78\x78\xd7\xf5\x98\xe3\xce\x07\xd3\x03\xe8\x8d\x0f\xb8\x37\x99\x92\xcf\xbb\xee\x6c\xcc\x3f\xff\xe5\xff\xfd\xf5\xf4\xf7\xeb\xcb\x63\xf0\x5f\x9a\xe2\x8e\xc2\xf8\x05\x76\x11\x11\x72\xce\xcc\x08\x01\xe6\x60\x7b\xd0\x1d\x6c\x3f\x57\xbc\x50\x7f\xbe\x3a\xff\x70\x75\x7d\x7a\x79\xa5\x99\x21\x3f\xaa\x4c\x77\x32\xb1\x20\x05\xa4\xda\xf7\xa6\x7b\x94\xed\x75\xe7\x38\xec\x1e\x50\x24\xa7\x6d\xc6\x6e\x9d\xfe\xbe\x3b\x9d\x88\xcf\x3d\xe8\x6c\x9b\x9b\x6c\x94\x3c\x56\xbd\x2a\x89\x30\xf4\xed\xdf\x2a\xf4\xc9\x35\xff\xc8\x16\xfb\x84\xdf\x8d\xfb\xfc\xc2\x7f\xfd\x79\x6f\xfc\x7b\x70\x72\xf0\x0a\xb6\xb6\xfe\x2f\x00\x00\xff\xff\xaf\x21\xd3\x0b\x60\xd3\x00\x00") +var _connector_mgmtYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x7b\x73\xdb\xb8\xb5\xf8\xff\xfe\x14\xf8\x29\xbf\x8e\xdb\x7b\x23\x59\x92\xe5\x97\xe6\xa6\x33\x4e\xec\x64\xbd\xeb\x38\x59\xdb\xd9\x6c\xda\xe9\xc8\x10\x09\x49\x88\x49\x80\x06\x40\x25\x4a\x7b\xbf\xfb\x1d\x00\x7c\x80\x24\x48\x51\xb2\x62\x3b\xbb\xe4\x4c\xbb\x31\x09\x1c\x1c\x1c\x1c\x9c\x37\x20\x1a\x20\x02\x03\x3c\x04\xbb\x9d\x6e\xa7\x0b\x9e\x01\x82\x90\x0b\xc4\x0c\x73\x00\x39\x98\x60\xc6\x05\xf0\x30\x41\x40\x50\x00\x3d\x8f\x7e\x01\x9c\xfa\x08\x9c\x9d\x9c\x72\xf9\xea\x96\xd0\x2f\xba\xb5\xec\x40\x40\x04\x0e\xb8\xd4\x09\x7d\x44\x44\x67\xeb\x19\x38\xf6\x3c\x80\x88\x1b\x50\x4c\x04\x07\x2e\x9a\x60\x82\x5c\x30\x43\x0c\x81\x2f\xd8\xf3\xc0\x18\x01\x17\x73\x87\xce\x11\x83\x63\x0f\x81\xf1\x42\x8e\x04\x42\x8e\x18\xef\x80\xb3\x09\x10\xaa\xad\x1c\x20\xc2\x8e\x82\x5b\x84\x02\x8d\x49\x02\x79\xeb\x19\x68\x05\x0c\xcf\xa1\x40\xad\xe7\x00\xba\x72\x16\xc8\x97\x8d\xc5\x0c\x81\x96\x43\x09\x41\x8e\xa0\x6c\xe4\x4f\x7d\xd1\x8e\x5a\x76\x16\xd0\xf7\x5a\x60\x82\x3d\xb4\x85\xc9\x84\x0e\xb7\x00\x10\x58\x78\x68\x08\x5e\xc5\x1d\xc0\x15\x62\x73\xec\x20\xf0\xda\x43\x48\x80\xb7\x90\xc0\x29\x62\x5b\x00\xcc\x11\xe3\x98\x92\x21\xe8\x76\x7a\x9d\xee\x16\x00\x2e\xe2\x0e\xc3\x81\x50\x2f\x97\xf4\xd7\xf3\xb9\x44\x5c\x80\xe3\xf7\x67\x12\x4d\x5f\x7d\x00\x09\xa2\xbc\xb3\xc5\x11\x93\x83\x48\xac\xda\x20\x64\xde\x10\xcc\x84\x08\xf8\x70\x67\x07\x06\xb8\x23\x89\xcd\x67\x78\x22\x3a\x0e\xf5\xb7\x00\xc8\x21\xf0\x16\x62\x02\xfe\x1a\x30\xea\x86\x8e\x7c\xf3\x37\xa0\xc1\xd9\x81\x71\x01\xa7\x68\x19\xc8\x2b\x01\xa7\x98\x4c\xad\x80\x86\x3b\x3b\x1e\x75\xa0\x37\xa3\x5c\x0c\x0f\xbb\xdd\x6e\xb1\x7b\xf2\x3d\xed\xb9\x53\x6c\xe5\x84\x8c\x21\x22\x80\x4b\x7d\x88\xc9\x96\x80\xd3\x88\x00\x04\xfa\x99\x75\xb9\x5e\x04\x88\x17\xfb\xb7\x5a\xb6\xd6\xb5\x1b\x82\x57\x5e\xc8\x05\x5a\xa1\x43\xb4\xbe\xd6\xf6\x5b\x01\x14\x33\x85\xff\x33\xf9\x3f\x60\xed\xf6\x6c\x6b\x0b\x80\x96\x5c\x86\x9d\x2c\x9b\xee\xcc\x7b\xad\xa1\x82\x3b\x45\x42\xff\x03\x80\x98\x20\xfa\x69\x97\x20\x02\xe4\x5e\x64\x50\x22\x72\xe6\x0e\x65\xff\xdf\x34\xbb\xbe\x45\x02\xba\x50\xc0\xa8\x15\x0f\x7d\x1f\xb2\xc5\x10\x5c\x22\x11\x32\xc2\xd5\x6e\x89\x38\x1b\xf8\xd9\xb6\x99\xc9\xd5\x68\xcf\x10\x0f\x28\xe1\xc8\x40\xb7\xd5\xef\x76\x5b\xe9\x9f\x40\xb2\xbb\x40\x44\x98\xaf\x00\x80\x41\xe0\x61\x47\x21\xbf\xf3\x99\x53\x92\xfd\x0a\x00\x77\x66\xc8\x87\xf9\xb7\x00\xfc\x7f\x86\x26\x43\xb0\xfd\x6c\xc7\xa1\x7e\x40\x09\x22\x82\xef\xe8\xb6\x7c\x27\x37\xfd\x6d\xa3\x73\x66\x5e\xbf\xe5\xe7\x92\xac\x5d\x91\xf3\xaa\x16\x6e\xe7\x16\x4e\x6e\xe1\x28\x7d\x2f\x64\xa7\x9d\x7f\x67\x5f\x8c\xb0\xfb\xbf\x11\x3d\x02\xc8\xa0\x8f\x44\xb4\xdf\xf5\xda\x6a\x56\x2b\x74\xd9\xb2\x62\x7e\x3d\x43\x00\xbb\x80\x2a\x89\x99\x76\x02\xb2\xd3\x56\x39\xe9\xe4\xe7\x21\xe0\x82\x61\x32\x4d\x5e\x63\x32\x04\x92\x75\x93\x17\x0c\xdd\x85\x98\x21\x77\x08\x04\x0b\x51\x7d\x9e\x4c\x37\x29\x00\x1c\x39\x21\xc3\x62\x61\xb6\x7c\x89\x20\x43\x6c\x08\xfe\x09\xfe\x55\xc2\xb7\x09\x2c\x09\xea\xe5\xe2\xec\x24\xcf\xb9\x6f\x90\x00\x30\x37\x5f\xa9\x45\x12\x3a\x65\xa8\xb4\xb4\xf5\x23\x71\x6d\xcb\xca\xb5\x99\xc9\xb7\x72\x5d\xd1\x57\xe8\x07\x9e\x89\x68\xfc\x64\xba\x9d\xea\x66\xc5\x56\xf6\xa1\x63\xa8\x3b\x36\x20\xad\xb2\x6d\x73\x5d\x60\x39\xe0\x43\xe1\xcc\xa4\xba\x90\xec\x28\xf9\x07\x29\xc9\x1f\x91\x74\xd0\xed\x3d\x0e\x49\x4f\x19\xa3\xac\x3e\x29\x07\xdd\xde\xba\x04\x4c\xbb\x96\x92\xed\x38\x14\x33\x20\xe8\x2d\x22\xd2\x20\xc0\x64\x0e\x3d\x63\x7b\xb7\x06\xdd\xc1\x0f\x42\xa4\xc1\xfa\x44\x1a\x2c\x23\xd2\x05\x4d\x79\x29\xc7\x63\xe8\x2b\xe6\x82\xa7\x04\xdb\x7b\xac\x8d\xba\x22\xc1\xf6\xba\xdd\x75\x09\x96\x76\x2d\x25\xd8\x07\x82\xbe\x06\xc8\x11\xc8\x05\x48\xe2\x05\xa8\xa3\xac\x2a\x77\x65\x7d\xb5\x8a\xf9\xb1\x61\x51\xcf\xcb\x2c\x14\x08\x3c\xcc\x85\xd4\x73\x59\x66\xe0\x55\x66\xca\xb2\x4e\x45\xed\x2b\x51\xb6\x2d\x44\xda\x72\x27\x80\x53\x63\x11\x96\x36\xe7\xf8\xdb\x2a\xcd\x29\x73\x11\x7b\xb9\x58\x65\x00\x04\x99\x33\x6b\x3d\x79\x45\x76\x8e\xb9\x28\x17\x89\x4b\x56\xaa\xd1\x1d\xf5\x74\x47\x23\x0a\x97\x8a\xc2\x9c\x5d\xbf\xa2\x45\x1f\x0b\xc7\x40\x7a\xbc\xcb\xa4\xe3\x3d\x04\xa3\xc3\x10\x14\xc8\xc4\x12\x98\x62\xf1\x95\xfa\xac\x82\x23\x5f\xd2\x2d\x63\x93\x85\x95\x2d\xed\x02\x50\xfa\x01\x77\x21\x62\x0b\x83\xbe\xda\x29\x81\x7c\x41\x9c\x32\xaa\xbf\x47\x6c\x42\x99\xaf\x2c\x3f\xa8\xa2\x0f\x00\x13\x00\x89\xee\x35\x63\x94\xd0\x90\x03\x1f\x12\x82\xd8\x56\x35\xb7\x69\xf7\x64\x4c\xa9\x87\x20\x31\xbe\x58\x1c\x12\x10\x5b\x99\x2f\xa9\x6b\x10\xb8\x24\x2c\x63\x38\xaa\xd6\xcd\x51\xbd\x35\xec\x1b\xa3\x96\x04\xbc\xd4\x48\x66\x77\x48\xd9\xfe\x48\x7a\xe9\xc5\x2b\xdd\x29\xf5\x2c\xf9\x0c\x90\x56\x95\x73\x57\xa6\x3e\xfa\x8f\xac\x3e\xca\xa5\xa1\xe3\xa0\x40\xa0\x8c\xf1\xfc\x63\x08\xc0\x41\xb7\xab\xd6\x05\x53\xb2\xbe\xb6\xc8\x83\x28\xa5\xd3\x6f\x52\x4b\xa8\x96\x5a\x20\xf2\x54\x22\x1a\x94\x6b\xf4\x6b\xe3\x9b\xd5\xf2\xcd\xae\x53\xdf\x1e\xb9\x52\x66\xd0\x90\x39\x08\xb8\x14\x71\xb2\x2d\xb4\x7f\xd6\xd8\x24\x39\xc6\x22\x20\x2c\x33\x4b\xb4\xb6\x8f\xa3\x26\x59\x25\x5d\xc7\x0b\xbb\x87\x9d\x21\xcd\xee\x22\x9c\x3f\x98\xf7\xf5\xa4\x7d\xa3\x55\xfd\xa2\xc6\x25\x6a\x5c\xa2\xc7\x89\x0e\xf1\x9d\x7f\x57\x67\x2e\x96\x6c\x46\xec\xb6\x1e\x42\xa4\x99\x31\xa5\xbc\x40\xcb\x25\x02\x6c\xe2\xcb\xde\xe4\x69\xca\x8e\x9a\x91\xf9\x26\x28\xdf\x18\x7e\x75\x88\xd4\x04\xe5\x57\x22\xd8\xbd\xc4\xae\x6e\xea\x21\x81\xbe\xa7\x2c\xd4\x23\x94\x8a\xc3\x13\xf5\x79\x99\x44\x2c\x6d\x65\x17\x8a\x4f\x65\xa3\x58\xe6\xd0\xb8\xbb\x7f\x58\xa9\xa7\x17\xf8\x1e\xb2\x2f\x03\xa0\x4a\x02\x2a\xab\x28\x56\xa3\xe0\x0b\x16\x33\xc0\x03\xe4\xe0\x09\x46\x2e\x38\x3b\xf9\x91\x25\xe1\xfd\x88\x98\x07\xb0\xa6\x54\x0c\xa4\x86\xf9\x9e\x42\x51\x0d\x50\x2a\x13\xdf\xcb\xaf\xcb\x44\x62\x59\xa3\xe5\xb1\xe8\x13\x28\x20\x10\x54\x23\x91\x2b\xda\x91\xbc\xb4\x55\xc1\x26\x26\x93\xf8\x88\x4d\x51\x5b\x41\xf9\xef\xba\x91\x6a\x1d\x56\xa7\xe3\xcf\xc8\x11\x25\x60\x25\xa8\x15\xa1\xe6\x1c\xd6\x9f\xaf\xde\x5d\x68\xfa\x3c\x07\x97\xaf\x5f\x81\xfd\xa3\x6e\x1f\xb4\x93\xba\x43\x41\xa9\xc7\x3b\x18\x89\x49\x87\xb2\xe9\xce\x4c\xf8\xde\x0e\x9b\x38\xb2\xd5\x7a\xd8\x6e\x3e\x44\x9f\x74\xfe\x23\x84\xc8\x1b\x4f\xe0\xcf\xab\x13\x1b\x4f\xe0\x47\xf0\x04\x2c\xb5\xa6\x51\x39\xf2\xaa\xd5\xa6\x4e\x54\xc5\xbc\x4a\x8e\x3a\x5b\xfa\x5c\x9d\x85\x4e\xd1\x02\xb5\x35\xef\x92\x94\x35\x70\x32\x30\x6b\xa4\xae\x73\x3d\xfe\x74\x29\xec\x68\xfa\x8f\x97\xca\x8e\xb8\x60\xcd\x8c\xb6\xee\xbc\x99\xc4\xb6\x05\xd6\x0f\x99\xdf\x8e\x26\xd2\xa4\xb9\x9b\x34\xb7\x41\xb9\xc6\xc6\xa9\x41\xa4\x26\xcd\xfd\xb4\xcc\x9c\x35\xd2\xdc\x19\x85\x5e\xab\xe8\x38\x67\xb2\xdc\x37\xed\x9d\x07\x57\x27\xfb\xed\x64\xfb\xd4\x4e\x80\xe7\xfa\x3d\x74\x05\xf2\xd3\x4c\x63\x45\x0b\xb0\x72\x85\x70\x8e\x98\x8d\x74\x6f\x32\xe2\x0f\x7c\x5e\x22\xe6\x40\xf3\x88\x5f\xf4\x6e\xc5\x53\x7e\x69\x2f\xbb\x03\x50\x76\xd0\x2f\xeb\x0d\x3d\xfc\x59\xbf\xfb\xcb\x62\x33\x5f\x9f\xf3\x30\xcb\x4e\xfb\x55\x38\x8d\xd5\x4d\x9f\xb4\xfc\xab\x19\xc3\x8b\x1d\xc0\x26\x96\xd7\xd8\xb9\x75\x88\xb4\x66\x2c\x2f\x66\xb3\x26\xa6\xb7\x6e\x1e\x2b\x7c\x10\xf1\x19\x06\xae\x25\x46\xf7\x72\x71\xe6\xe6\xa5\x68\xe8\x06\x30\x9b\xc7\xaf\x12\xa4\x4b\x5b\xd7\xcf\x75\x69\x14\xdd\x35\x33\x5d\x0f\x12\xbc\x5a\x21\x5a\x94\x15\x19\xd9\x28\x5d\xb4\x67\xb8\x80\x22\x54\xf7\xa3\x44\x53\x6f\xe4\x72\x23\x97\x37\x2c\x97\x1b\x91\x6c\xa3\xd9\x46\x0a\xae\x36\x20\x95\x73\x85\x57\x25\x76\x6d\xb1\xb2\xaa\x4a\x22\x2f\x6d\xdd\xd4\x63\x35\x72\xf1\x89\x10\xe9\x01\xeb\xb1\x92\xc0\x6c\x53\x8a\xb5\xc9\x52\xac\xcd\x45\x41\x76\xa0\xeb\x52\x32\x4a\xa3\x20\x4d\x58\x64\xbd\xb0\xc8\xb1\xa4\xe3\xfb\x84\x6a\x79\x6d\x52\x12\xfa\xd8\xe6\x40\x2d\x80\x41\x6f\x9b\x76\x59\xb9\xf7\x93\x8a\xa5\x64\x49\x53\x19\x49\x96\x2c\x93\x4e\x06\x88\x19\x14\x80\xcf\x68\xe8\xb9\x60\x8c\x40\xc8\xf5\x6d\x83\x0e\x25\x13\x3c\x0d\x19\x52\x8c\xa5\xef\xe9\x33\x3d\x18\x4d\x14\x4a\x34\xdf\x69\x5a\x75\x1a\x75\xf6\x47\x55\x67\x4d\xf8\xe5\x47\xb2\xf5\x37\xa8\xbb\xa4\x42\xe2\x01\x74\xd0\x0f\xae\xb5\x56\xcc\x2b\xae\x94\x55\x5c\xf5\x56\xa3\x95\xee\x34\x7a\x3c\x75\x7b\x91\x2c\x7d\x7d\x4d\x4b\xf2\x7d\x6a\xea\xd8\x42\xbf\x27\xa5\x5d\x13\xca\x24\x24\x59\xaa\x61\xd3\x09\x81\x39\xe6\x78\xec\xa9\xeb\x84\x43\x8e\x18\xc0\x8d\xd2\x6c\x94\x66\xa3\x34\x9f\xa0\xd2\xcc\xd7\x21\x67\x24\xe0\x4a\xa5\xc8\x45\xb5\x59\xab\x18\xb9\x20\x72\xab\xca\x91\x93\xc6\x2b\x49\xfd\x65\x05\xc9\x24\x07\xb5\x4e\x49\x72\xbe\xcf\x2a\xf5\xbc\x49\xdf\xc7\xab\xe8\x4d\x08\xb9\x5e\x4d\x6f\xd2\x7d\x23\x55\xbd\x76\x68\x3f\x64\x5d\x6f\x32\x95\xa6\xb2\xb7\xa9\xec\x35\x28\xd7\x58\x0f\x35\x88\xd4\x54\xf6\x3e\x2d\xc3\x61\x9d\xca\xde\xac\x5e\xac\xe5\xc9\x15\x9d\xae\x7b\x56\xf7\x96\x7b\x71\x55\x75\xba\xd5\x7e\xdc\x4a\x3d\x9b\x5b\x86\xd3\xe7\x09\x38\xa7\xb6\x42\xe2\xc2\x9a\x35\xea\xa4\x29\x25\x7e\xe0\x40\x64\xca\x83\x66\x28\x32\x79\xbb\x62\x39\xb1\xd9\xcf\xee\x81\x94\xc5\x20\xf3\xbe\xcc\xc3\xe7\xce\x36\xa1\x02\xcc\x70\x5e\xc1\x4f\x2c\x0b\xe3\x55\xba\x7e\xcb\x1a\x3f\x71\x99\x58\xb3\xb8\x38\xf5\x46\x9b\xf2\xe2\xc6\xd8\xae\x43\xa4\x35\x43\x75\x29\xa3\x35\xc1\xba\xef\x59\x60\xbc\x09\x61\x9a\x2b\x31\x4e\x40\xd6\x2c\x32\xae\x14\xab\x35\xda\xff\x90\x85\xc6\xe5\x31\xb5\xcd\x94\x1a\x27\xf0\x9b\x62\xe3\x46\x4a\x3f\x80\x94\x6e\x04\xb4\x8d\x6a\x9b\x29\x37\xde\x84\x8c\xce\x15\x1c\x97\xda\xbc\x96\x22\xe2\x4a\xf9\x5c\xa3\x7d\x53\x76\xdc\x48\xc8\x27\x42\xa4\xa6\xec\xf8\x4f\x54\x76\x6c\x44\x4c\xd0\x1c\x7a\x1b\x4f\x34\x9f\xce\xa1\x17\xaa\x77\x9b\xcc\x34\xf3\x19\x65\x02\x78\x78\x2e\xe7\x9e\x8c\xb0\x56\x02\xba\x56\xf7\x1f\x35\x17\x2d\xa9\x7f\xcf\x7c\xb4\x04\xb1\xd9\x9c\x74\x01\x62\x93\x97\xfe\xde\x58\x37\x79\xe9\x07\xa3\x5c\x63\x62\xd4\x20\x52\x93\x97\x7e\x5a\x2e\xd8\xfd\xf2\xd2\x5b\xe9\xa8\x12\xb9\x68\x86\x43\x7d\x07\xf0\x33\xfd\xff\xe0\x15\xf5\x7d\x4a\xa2\x57\xea\x3f\xe7\x38\x35\x32\x12\xc1\x6f\x18\x03\xb7\x98\xb8\xc6\x9f\x01\x9c\x22\xe3\x4f\x8e\xbf\x99\x7f\x0a\x2a\xa0\x67\xfc\x8d\x05\xf2\x63\xb3\xc4\x72\x09\x72\xc0\xa4\xad\x22\xb0\x49\x6a\x39\xde\xd2\x04\x8d\xc4\xa2\xd8\x08\x13\x81\xa6\x66\xcd\x39\xfe\x56\xa3\x95\xc2\xb9\xbc\x99\xfa\xa0\xd8\x24\x6e\x03\x3d\xef\xdd\xc4\x24\x51\x15\x83\xbd\x53\xf3\xbd\x44\x13\xc4\x10\x71\x32\x99\xed\x92\x5b\xa1\x6d\x44\x01\x6a\x4f\xb8\x05\xae\xb3\x12\x07\xa8\x95\x84\x96\x1d\x52\xda\x3c\x31\x19\x47\xd8\xad\xec\xa4\xbe\xe5\xe6\x34\x5c\x6d\x81\xf1\xf2\xe5\xad\xc5\x03\x33\x49\xf5\xb2\x46\x06\x9e\x6f\x91\x80\x2b\xa2\x48\xbf\x10\xc4\x96\x22\xa0\x4d\x6b\x77\x04\x33\x72\x6a\x42\x99\x0f\xc5\x50\x9a\x9d\xa8\x2d\xb0\x8f\x96\x81\xf1\xa9\xab\xdc\xad\x75\xe1\xa8\xf7\x57\x88\xcd\xb1\x13\x07\x4d\x30\x25\x57\x48\x48\x69\xc1\xab\xb6\x36\x36\x37\x76\xc8\xbc\xfb\x2d\x5a\xc8\x2c\xbb\xc8\x82\xe3\xb1\xe3\xd0\x90\x54\xca\x1c\xc7\xc3\x88\x88\x51\x06\xbf\xe8\x1d\x47\x0e\x43\x55\x6b\x97\xf4\x5d\xbe\x7e\x26\xc4\x6a\xd4\x4f\x50\xe0\xd1\x85\x8f\x88\x38\xa7\x5a\x03\xad\xc8\x51\x66\x12\xbb\x7a\xa8\xdf\x10\xe3\x98\x12\xc9\xb5\xd2\x73\x79\x20\xa1\x83\x6c\x5a\x4d\x6d\x43\xd0\x3a\x7e\x7f\x16\x21\x95\x55\x94\x58\x7e\x9c\xf7\xb2\x2f\x67\x1a\x2d\xbb\xdf\xdb\xca\x09\x34\xcf\xd3\xcc\x5a\xd0\xb4\x6d\x0d\x5c\xb9\xc9\x3c\xaf\x9e\x97\x0c\x52\xfc\x9d\xe3\x42\xff\x68\x62\xa5\x3f\x5c\x57\x2e\x82\x4b\x31\xd6\x74\x85\x8c\xc1\x45\xee\x8b\xd2\x81\x45\x73\x21\xb7\xa0\xe6\xdc\x57\x5a\xda\x8c\x7a\x8f\xb6\x18\x37\x15\xfc\x2f\x92\x1c\xe5\x82\x21\x63\x81\xfc\x44\x3d\x97\xc7\x16\x86\x3a\x34\xaa\x3d\x02\x7d\x8a\x54\x42\x90\xff\x84\x1a\x26\x38\x23\x5c\x40\xe2\xa0\xce\x3a\x3c\x5a\x2a\xb1\xd2\x85\x78\x16\xfd\x40\x49\x14\x91\x72\x8c\x75\x49\xdb\x94\xb0\xf4\xb3\xec\x2a\x6a\x01\xa4\x86\xbe\x44\x53\xcc\x05\x5b\x6c\x98\x24\x0a\x38\x88\x81\x3f\x00\x6d\x74\x63\xc0\xe2\x11\x37\x45\xa5\x98\x97\xd4\x39\xe4\x0c\x27\x65\x4f\x26\x5b\xa9\xd5\x3a\xce\x9f\xb1\x6e\x6d\xdc\x3a\x98\x43\x2f\xb4\xd8\x75\xa6\x10\x2d\x9e\xa1\x2e\xc3\x36\xae\xa0\xcb\x9f\x0c\xcf\xa2\x6d\xee\xeb\xdc\x7e\xae\x7f\x94\xbb\x95\x37\xc5\x8b\x77\xe4\x27\xa4\xce\x1f\xe0\xbb\x12\x50\xe4\x0c\xad\x0c\x55\x10\x09\x7d\x93\xbb\x5c\xcc\x23\xee\x44\xa6\x12\x65\x08\xba\x0b\xfb\x08\x51\x80\xca\xb4\x96\xca\x14\x59\x35\xed\x4b\x00\xdb\x17\x40\x6f\x49\x69\xec\x98\xc5\x39\x69\x02\x1c\x40\x15\xbf\x03\x81\x07\x09\x32\x0e\x19\xea\x4c\x71\x6b\x9d\xcd\x55\x31\xf1\x96\x7d\x06\x26\x4d\xd6\xd0\xc3\x1a\xf2\xf7\x42\xee\x4a\x51\xa2\x6a\xc9\x78\xa6\x45\xc9\x5e\x2c\xeb\x1c\x03\x28\xb8\x1e\xab\xcc\x42\x71\x6f\x2e\xf4\x69\x7a\x54\xf5\x99\x69\xd3\xe6\xd0\x2a\xb3\xb8\xcf\x3a\x5e\x45\xfc\x6a\x9d\x94\x29\x9f\x56\x9a\x58\xd6\x6e\x59\xd9\xa3\xb4\x5a\x26\x2b\x1b\x32\xab\xdd\x0b\x6a\x17\x81\xc6\xdb\x57\x33\x48\x08\xf2\x2a\x64\x9d\x8b\x26\x30\xf4\x84\x7c\x0b\xc7\x1e\x2a\x91\x80\xd1\xc7\x2c\xc1\x4f\x10\x97\xbe\xc6\xaa\xd2\x54\x8b\x4d\x13\x36\x0d\x82\x8c\x60\x75\xa3\x6c\x6c\x76\xb8\x55\xc7\x81\x9c\xe3\x29\x31\x75\x5d\xfc\x2e\x33\x98\x12\x8d\xd9\x56\xcb\x31\x9c\x40\xec\x15\x51\xce\x42\x71\x73\x39\xe5\xb6\x64\x9d\x39\x96\xa6\x7f\xbe\x61\xe6\x43\x8e\xab\x4d\x3b\xa9\x32\xb4\x24\xad\x3b\x13\x69\x6d\xf6\x8c\xa0\xf6\x10\x8d\x2f\xf9\x1f\x27\xb3\x06\x8e\x24\x34\x93\x3d\xab\x18\xb3\xc4\x28\x4e\x37\x53\x0e\x97\x22\xdc\xed\x2a\xcb\x2d\xf2\x71\xb7\x53\x70\xea\xfb\x28\x36\xd6\xea\xa2\xb9\xcc\x62\x4d\xf1\x4d\x28\x64\x82\x7e\x66\x04\x0a\xab\xcc\x43\xd9\x52\xa9\x59\x3e\x83\x01\xca\xbc\x0e\x18\x75\x10\xe7\xe6\x8f\x75\xcb\xd7\x3a\x38\x39\x83\xc4\xf5\xb2\xb1\xa4\x8c\x08\xca\xf2\x85\xc5\xc2\xb0\x71\x85\xb4\x30\x6c\x4b\x3f\x92\xa0\xb3\x31\x01\x37\x71\xcb\x47\x5e\xe4\x97\x67\xbe\xaa\xcd\x3e\x52\xea\x6b\x5d\x93\xa6\x40\xdf\x18\x8d\xe5\x3d\xb2\x82\x6c\xd9\x52\x47\x72\x2f\x5d\x51\xcb\xe4\xea\xc2\x2a\x86\x2b\x4c\xb0\x06\x55\x6a\x23\x67\x13\xa0\x79\x6d\x96\x33\xf4\xd6\x33\xca\x32\x06\xcf\x8a\x7d\x33\x82\x27\x8f\xdd\x63\x18\x71\x25\x93\x59\x51\x4d\xc7\xc9\x93\xd1\x5c\x47\x61\xec\x1a\x3b\x1f\xd6\xd6\x4f\x1c\x45\xc4\x44\xec\x0f\x2c\xda\xe9\xc9\x5a\x8e\x1b\x30\x19\x1f\xc5\x56\xdc\x04\xe3\xae\xd8\xdb\x6e\x5b\xfe\x09\x8c\xca\x96\xdd\x98\x04\xd7\x8b\x20\x1b\x02\x4b\x3e\xc9\x2f\x56\x47\xf4\xef\xed\x04\x85\x4b\x14\x30\xc4\xe5\x88\x99\x52\x41\x55\xdb\xcf\xc3\x20\xa0\x4c\x20\x17\x8c\x17\xca\x61\x3d\x7e\x7f\x16\x75\xa4\x04\x65\x69\x5c\xd4\x6d\xa0\xa8\xdf\xf4\xab\x68\x63\xe7\xde\xea\xf9\x6e\x12\xe2\x67\x4e\xc9\x28\x03\xf6\x91\x72\x59\x79\x95\x5b\x58\x8f\x0b\xe8\xa3\xe2\x81\x2e\x39\x4a\xa7\x4a\x00\x98\x1f\x4a\xa4\x65\xb6\xe8\x41\xb7\xb9\xe7\x48\x91\xa6\x2f\xb0\x71\xb6\x34\x29\x6a\xb4\xca\x58\x9b\xda\x30\x79\xd3\x22\x8f\x5c\x15\xde\xc7\xe6\x9f\x05\xe4\x6b\xd3\x08\x3b\x94\x8c\xf2\x29\xbb\xc2\x60\x1f\x2e\xcf\x55\x34\x95\xa8\xf6\xeb\x8f\xe6\xc1\xf1\xb2\xf5\x38\x57\x4d\xd2\x4b\x12\xa1\x40\x53\xca\xf0\x37\x94\x1d\xf2\xbe\xeb\x52\xce\x34\x30\x80\x63\xec\xe1\xe2\xe6\xb0\x9d\x6a\x33\x1a\x17\x85\x90\x23\xd7\xfb\xbb\x22\x6b\x2f\xad\x28\x93\xa0\xf1\x73\xac\x04\x4e\x1c\xa8\x56\xb7\x53\x3a\x90\x98\x57\x53\xce\x75\xd1\x11\x02\xb0\x60\x46\x16\xa0\xa5\x1b\x66\x82\x91\xe7\xda\x79\xa1\x20\x81\x80\x29\xf4\x7e\x9c\x09\x14\xd5\xd6\x9f\x40\x9f\xcb\x69\x96\x06\xc9\x73\x65\xae\xcf\xb2\x14\xca\x9f\x51\x5a\xc1\xc9\x5c\x21\x83\xbb\xd4\xe7\x83\x84\x50\x01\x0b\x09\x42\x3b\xb9\x2c\xa4\x2a\x65\xe2\xb2\xd5\xb1\xab\xd2\x8a\x9d\x6c\x49\x9f\x2c\xe9\x61\xb7\x3a\xac\x76\x87\xb2\x3c\x24\xf8\xad\x92\xd5\x79\x0c\x27\xcc\xc6\x1a\x79\x63\x39\x69\x73\x8d\x08\x4c\xa3\x3d\x96\xe5\x58\x52\x22\xe5\x62\x29\x51\x7c\x4c\x60\x26\x14\x13\x2d\xdf\xe2\x42\x9d\x6d\xcf\x94\x54\xf9\x30\x08\x30\x99\x9a\xd4\x0d\x39\x62\xf5\xa7\xa5\x51\xfe\xc0\xb3\xbf\xba\x44\xd9\x14\x12\xcc\x61\x94\x67\x59\x09\xd6\x3b\xa3\x6f\xab\xcc\xbc\x5d\x8d\xf6\x36\x24\xd7\x83\x91\x45\xae\x72\x19\xe5\x70\x2b\xd6\x69\xd4\xaa\x3c\x92\xeb\xb3\xb4\x8e\xa3\x0e\xfe\xdf\x03\x39\x73\xe1\xd7\x40\xb2\x2a\x5b\x77\x6c\x3d\x4e\xa7\xcb\xa0\x72\xe7\xea\xeb\x04\xf6\x8a\x37\xcb\x66\xb6\xd2\x7a\xf1\xa3\xea\xa2\xfb\xd5\x8b\xee\x12\x24\x2b\x03\x0f\xe6\x87\xfc\x2a\x55\x36\xce\x51\xf8\x1d\x51\x5e\xcf\xb6\xe4\xb0\x6d\x40\x19\xd8\x36\x97\x73\x1b\x70\xec\x63\x0f\x32\x69\x79\x6c\x97\x31\xd8\x76\xd9\xd2\x1a\x14\xb1\x2f\x2f\x59\x72\x0c\xc3\xbe\xd4\xf7\x5c\xa7\x3a\x52\xf9\x4f\x60\xfb\x18\x07\x19\x4a\x88\x50\x7f\x4b\xae\xaf\x60\xd7\x8f\x7b\x65\x54\xfc\x86\x23\x03\xab\xba\xf8\xf5\x03\xa2\xe6\x97\x35\xb6\x3a\xfa\x1a\x60\x56\x88\xcc\xcb\xe7\x59\x55\xf9\x66\x25\x4c\x91\xb1\x40\xe2\x67\x1d\x4d\x69\x9a\x03\x65\xa1\x23\x43\xf8\x82\xd5\x62\x49\x16\xf1\xad\x3f\xe8\x09\x48\x2e\xce\xdf\xee\x93\x52\x56\xdf\xf1\x93\x74\x2d\xf8\xbb\x67\x27\x52\x0c\x32\xe4\x50\x96\x5c\xcb\x90\xf3\xe0\x2c\x04\xcc\x5d\xdb\x63\x39\xc6\x64\xd6\x8d\x6b\x1c\x8c\x7a\x76\xd9\xfd\x2e\x44\x6c\x61\xc3\xea\x3d\x9c\x22\x80\x89\x8b\xbe\x16\xa0\x4f\xa0\xc7\x51\x7d\x2c\x8b\xa7\x0b\xf2\xd5\xec\xda\x42\x07\xad\xa8\x5e\xd2\x2c\x63\xd7\x48\x1b\x55\xf7\x95\x48\x5f\x84\xfe\x18\x31\x49\x4a\x25\x9a\x00\x26\x00\x41\x67\x66\x4e\x7a\x83\xd3\xc8\x97\xdb\x27\xd3\xe8\x76\xf5\x44\xa2\xbb\xda\xac\x82\xec\x3f\xa9\xf7\x7d\x15\x1d\x40\xd5\x55\x77\xaa\x13\x18\x2f\x80\xc3\xb0\x40\x0c\xc3\x8e\xe2\x10\xbe\x20\x02\x7e\xd5\x11\x22\xcc\x53\x56\x03\x98\x1b\x08\x25\xda\x52\xe4\xba\x20\x70\x13\x03\xbe\x01\x8e\x07\x43\xae\x14\x2f\x24\xe0\xea\xd7\x73\x9d\xb6\xf0\x11\x11\xa9\x8f\x7e\x2a\xe9\xa6\x08\x1d\x87\x00\x54\x7f\x1d\x84\x81\x64\x91\x80\xcd\x78\xb3\x37\xda\xd5\xe7\x29\x9c\xd7\x94\xc5\xa4\x7b\x2e\x11\x63\xea\xfe\x3d\x29\xab\x0d\x5f\x57\x92\x9b\x9b\x03\x88\x19\xc2\x5a\xc0\x3f\x97\xd6\xa7\x1a\x69\x42\x3d\x8f\x7e\xc1\x64\x1a\x4d\x2c\x2a\xdf\x93\xcf\xcd\xcd\x0d\xbf\x4b\xcf\x61\xc8\x7e\x00\x72\xc7\xfc\x9e\x36\xbe\x5e\x1d\x09\x30\x82\xc4\x1d\xc5\x82\xe1\x3e\x28\x3d\x8f\x81\x94\xe3\x77\xa6\x09\x6b\xae\x30\xd9\x16\xba\x32\xc1\x45\xee\x73\x69\x26\x61\xdd\x46\x71\x1c\xc0\x1c\x20\x3f\x10\x8b\xe7\xf2\x5d\x2a\xb6\x74\x7d\x19\x0f\x3d\xc1\x01\x64\x99\xf5\x93\xd8\x74\x12\xbe\x0e\x3c\xea\xa2\xcc\xd9\xc7\x22\xaf\xe7\x58\xd9\x64\xf7\x78\x6a\xad\x92\x1d\xaa\xb7\x70\x04\xe0\xbe\xbb\x90\x8b\x85\x87\x86\x4a\xab\x69\x59\xa1\x2e\x37\xb4\xef\xb0\x74\x83\xa9\x46\xe9\x86\x32\x78\xa1\x7a\x67\x2d\xd9\x51\x5f\x66\x88\xa1\xcc\x76\x4a\x87\xcc\xec\x2a\x70\x2c\xf9\x04\xb9\xd1\xee\x88\xaf\xd1\xd7\xc8\xab\xc5\xb9\x91\x54\xba\x79\x0e\x6e\x8c\x29\xc8\x3f\x23\x6e\x91\xff\x54\x31\xde\x9b\xe7\x00\x12\x17\xdc\x44\x21\xf8\x9b\x74\xa3\xc5\x43\xe8\xa3\x2d\x94\xe9\x45\xbf\xf9\x9f\xbf\xcb\xbe\x2f\x6e\x14\xdb\xdc\x9c\x9f\xfd\x72\x6a\xe9\xe3\x50\xf2\x39\x24\x8e\xc0\x73\x94\xef\x7f\x7c\x71\x72\xa3\x87\x7c\x77\x79\xd3\x01\x3f\xd1\x2f\x68\x8e\xd8\x73\xb0\xa0\xa1\x12\x0c\x72\xe6\x10\xf8\xf0\x2b\xf6\x43\x5f\xd2\xa0\xd7\x4d\xc1\x51\xa2\xe6\x0a\xe3\x99\x2a\xb6\x30\xc8\x7f\x9a\xf0\x99\x6d\x77\xe6\x32\x5c\xfa\xb4\xbe\x88\x7e\xa0\x00\xdc\xc0\x2f\xbc\xcd\xef\x78\x5b\x27\x8b\x35\x92\x2a\x3a\xac\x49\x03\x6e\x74\x45\xd4\x4d\xdd\xed\x9a\xdd\xab\x2f\x40\x16\xbe\x02\x1f\x83\x7e\x91\x2d\xc5\x52\xdd\xff\x19\xb4\xff\x65\x9f\x86\x2e\x1e\xc7\x51\x81\xb4\x9e\x06\xd4\xa3\xe8\x5f\x30\x12\x90\x09\xae\xdf\xcb\x59\xad\x89\xb1\x87\x6f\x91\x44\xfa\x2f\xfd\xbd\xef\x22\x58\x94\xb8\x94\x1f\xb3\xcb\x62\xc8\x1b\x28\xd4\x77\xf5\x4b\x11\x33\xc8\x41\x80\x98\x8f\x39\x8f\xaa\xc7\x39\x42\x8a\xa5\x34\x5d\x90\x6b\xf0\xc1\x05\x15\xa8\x13\xe3\xa7\x95\x4e\x7a\xd2\x54\x72\x7c\x54\x7f\x83\xb9\xd1\xbb\x5c\x7c\x45\x46\x83\xe2\xb9\x12\xa1\x64\x17\x40\x16\x1d\x9f\x91\x2f\x20\x2f\xf6\x6a\x71\x49\x6b\x3d\xf1\xb6\x95\x5e\x56\xa0\xca\xa2\x62\xb4\xa2\xdb\x0a\x4c\xa0\x68\x08\xc6\xea\x6d\xf4\x52\xff\xf1\x3a\x32\xc9\x7f\xfe\x78\xbd\x65\x8e\x38\x13\x22\x90\xd0\xb3\xb3\xcd\x97\x2e\x5a\x4f\xdf\xe7\x62\xa9\x9a\xd0\xad\xb7\x8b\xcc\xcf\xa0\x67\x2c\x82\x6a\x00\xd8\x1d\x02\x8f\x4e\x47\x1c\x93\xdb\x51\xb7\xd3\x4b\x3e\xe8\x13\x2b\x19\x48\xc9\xb7\x95\x4e\xc3\xa8\xf2\x25\xbe\x63\x0e\xd2\xca\xe1\x7f\x4e\xa7\xe0\x0a\x93\xdb\xe4\x75\xec\x66\x81\x56\xa6\xb5\x2d\xed\xd9\xce\x4b\x82\x6c\xce\x2d\x0f\x39\xcd\x0a\xae\x89\x7f\x27\x20\xd3\x14\xa3\x62\xda\xaf\x0d\xb8\x39\x5e\x59\xd2\xad\xad\xca\xdf\x46\xf9\xf2\xb7\xb6\xad\xfc\xad\x98\x4a\x2a\x3f\x2e\xe4\xfb\x45\xd7\x30\xdd\x6a\xe9\xfd\x1a\xf1\x23\xb0\xf0\xf4\x0a\x58\xdd\x45\x4b\x62\xa0\x2a\x35\x00\x80\x1f\x7a\x02\x8f\x3c\x4c\xac\xe7\x95\x93\x42\x5a\x73\xcf\x67\x1b\x18\x8b\xf7\x56\xc2\x02\xe7\x98\xd8\x5a\x46\x88\x57\xb7\x51\x73\x18\x53\xea\x21\x48\x2c\xdf\xbf\xb6\xa7\x8c\x86\xc1\x10\xb4\x10\x71\x03\x8a\x89\x28\x9e\xdc\xe2\x33\xfa\x65\x04\x3d\xef\xfe\xd3\xb9\x9a\xd1\x2f\x52\xe1\x97\x4f\xa6\xaa\xc5\x3d\xa7\x22\x68\x80\x9d\x25\xf5\x02\xd4\xf7\xa5\xa1\x20\xd5\x93\x40\x6e\x72\x50\x45\x6b\x4f\x05\x40\x47\x7c\xec\x2c\x74\x5d\xde\xa0\x3c\x16\x99\xa2\xad\x76\x5d\x16\x67\x2e\x50\x70\xff\x60\x58\x2e\x8f\x90\x3e\xed\x4a\x46\x8e\x60\x12\x8e\x98\x18\x29\xab\xb1\xac\x4d\xb9\x5f\x59\x7c\x8e\x5d\x57\x15\xf9\x84\x5c\x50\x5f\x1b\xa3\xb1\x39\xe2\x50\x65\x9f\x88\x48\xf5\x47\x06\xaf\x8f\x38\xd7\x81\x00\x20\x18\x24\x1c\x8b\x7c\x16\x37\x7d\x96\x4f\x47\x3e\x4b\xe6\x52\x98\x4f\xfc\x83\x54\xb1\xd1\xad\x91\x16\x54\x7a\xa4\xd0\x75\x91\x5b\x09\x2a\x62\x8e\xd7\xb2\x53\x75\xc3\x72\x26\x31\x9f\x92\x5c\x62\x25\xf6\x9a\xa0\x26\xfa\x75\x50\xfe\x4d\xa5\x15\xef\x8d\x72\x59\x32\xd3\x7c\xda\x4b\xb1\x8a\xb3\x9c\x4b\x70\x3e\x53\xec\xaa\xa9\x0d\x8e\x1d\x91\x0f\xa2\x15\xb1\xb7\x0a\xf8\x7a\x98\xb7\x33\xbb\xc3\xda\x68\xc9\x18\x75\x76\x20\xfa\x2a\x18\x74\x56\xdb\x82\xa7\xba\x0f\x80\x11\xb3\x4e\x18\xf5\xd5\xe2\x8f\xa9\x9b\x97\x1a\xe9\xf3\xc7\xdf\x3e\x9b\xe0\xc5\x08\xa3\x98\xc4\x0f\xc5\x6a\x19\x36\xf8\x5e\xbc\x36\x83\x7c\x34\x43\xd0\x45\x6c\x34\xc1\x9e\x40\x85\xda\xdf\xf4\xc9\xac\xf1\x6b\xd5\x18\x8c\x21\x97\xee\xbf\x0e\x2d\xe8\x92\x4e\x47\xad\x3b\x25\x08\x68\xb8\xf7\x64\x3e\x7b\xe9\x45\x29\x5e\x92\xf7\xf4\xb8\x91\xb3\x4b\xe3\x7c\x5b\xb5\x60\x8b\xcf\xe6\x47\x9d\x2f\x8a\x55\x17\xd9\x27\xe2\x89\x9f\xf4\x50\xcb\x9b\x6f\x8e\x57\x2d\x05\x21\x45\xb4\x20\x8f\x51\x8b\x16\xea\xfb\xb3\x6b\x81\x93\xea\xb1\x6c\xea\x02\xd6\xf6\xfd\xde\x2e\xce\xe9\xd4\xcc\x3a\x2d\x39\xc5\x91\xb9\x17\x02\xb4\x8e\xc6\x7c\xde\xe5\x07\x82\xa0\x83\x69\xb7\x3f\x9d\xed\x4d\x07\x86\x7b\x53\x38\xe1\x64\xf4\xd9\x1f\xb3\x09\xeb\x76\xfb\xc1\x84\xdc\xce\xba\xa6\xe5\x96\x5e\x88\x01\x5a\x9c\xcd\x9d\x36\x74\x1c\xd1\xee\xed\xf7\xd1\xa4\xef\x1e\xb6\xbb\xfd\xee\x51\x7b\xd0\xeb\x1d\xb4\x0f\x07\xfb\xfd\xb6\x3b\xd9\xdf\x75\xfa\xdd\xfe\x9e\xd3\xdf\xb7\x40\x89\x2e\xcb\x00\xad\x71\x6f\x30\x70\x8f\x8e\x7a\xed\xee\x21\x1a\xb7\x07\x83\x83\x7e\xfb\x10\x39\xbd\x36\x1a\x77\x77\x07\xce\xfe\x51\x7f\xb7\x37\x36\xfb\x87\xcc\x1b\x82\xd6\x84\xd2\xb6\x0d\xdf\xce\x2d\xe4\x1d\xe8\xf8\xa8\xe3\x50\x7f\x38\x18\xec\xb6\x72\xee\x96\xf5\xe4\x94\x31\xfd\xee\xed\xa1\x47\xa6\xdd\xdd\x1e\x47\x47\x77\x35\xa6\x8f\xba\xfd\xbd\xfe\xfe\x1e\x6a\xc3\xc3\x43\xd8\x1e\x0c\x26\xe3\xf6\xe1\x60\xaf\xdb\x46\x6e\xb7\xd7\x45\xe3\xfd\xb1\xb3\xe7\x54\x4d\xdf\x75\xf6\xe0\x61\xff\xe8\xb0\x3d\x46\xee\x41\x7b\xd0\xef\xa3\xf6\xe1\xd1\xe0\xa0\x3d\xd9\x9f\xb8\x70\xff\xa8\x7f\xd4\x9f\x4c\x8a\xd3\x1f\x43\x16\x4d\xbf\xef\x4f\x1c\xd8\xed\xf6\xc5\xd1\xdd\x01\x9f\x76\x38\x2b\x9b\x7e\x7c\x8a\x28\xef\x57\x17\xcf\x23\x81\x96\xdd\xa9\xb7\x9e\x0c\xb3\xb9\xa6\x89\x6f\x65\xc6\x8e\xf4\x93\xfa\x91\xbc\xf0\x35\xf2\x65\xd4\xe2\x3e\x1f\xc3\xec\x8d\xb5\x89\x57\x9d\x1d\xea\x16\x2d\xf2\xbb\x35\x4e\x98\xb6\xae\xae\x2f\xcf\x2e\xde\x64\x7d\x0f\xab\x9d\x99\xf4\xf8\xf9\xea\xdd\x45\xee\xfa\x8e\xc8\x69\x2f\xa4\x3b\x2b\x1d\x88\x28\x7c\xa3\xbe\x4a\xb1\x59\x74\x3f\xe3\x60\x97\x6a\xa2\x4c\xd2\xb2\x83\x55\xb9\x13\x99\x2a\x5e\x37\x8a\xcf\xcb\x99\x43\xbb\x08\xba\x23\x0f\x09\x81\xd8\xe8\x2e\x44\xf9\x69\x2a\xea\x4a\x86\xf3\xee\x72\xd1\xa4\xea\xdf\x2a\x2c\x8b\x4c\x59\x2e\x4c\x34\x12\xdf\xcb\x24\x50\x49\x7d\x63\xfc\xa3\x2d\xad\x6c\xf8\xa6\x03\x03\xdc\xa1\x01\x22\x7c\x86\x27\x42\xf2\xf6\x4e\xc0\xe8\x04\x7b\xc8\xb6\xba\xa0\x15\x39\xf0\xed\x4c\xa3\x15\xee\xc2\x2c\x9b\xb3\xec\x60\x99\xf7\x23\x4c\xa6\xfc\x4a\x47\x4b\x20\xb0\xd5\xeb\x1a\x82\x20\xba\xb3\x26\x77\x61\x5d\x75\xec\x4c\x5f\xe4\xb8\x93\x81\xa3\xae\x11\x03\xad\x57\xef\x2e\x2e\x4e\x5f\x5d\xbf\xbb\x6c\xbf\x7d\xf3\xf6\xba\x9d\x69\x12\x5d\x1e\x06\x5a\x57\x0b\xe2\xcc\x18\x25\x34\xe4\x00\x2a\x2d\x0d\x30\x07\x84\x8a\xb4\x06\x5d\xc7\xe6\x21\x5f\x10\xe7\x85\x14\x0c\xc5\x8b\x3f\x72\xb7\x8b\x81\x56\x0f\x7f\x3c\xc3\xfe\xdd\x1b\x87\x9d\x84\xe7\xfb\x3d\xf8\xe1\xeb\xd9\x3f\xee\x5e\x5e\xdf\x5d\x5c\xc2\x84\x4a\x67\x3a\xd6\xfd\x6b\x88\xd8\xa2\x06\xa5\xfa\x1b\xa2\x54\x7f\x29\xa1\xfa\x16\x3a\xfd\xc7\x60\x80\xd7\xea\x98\xb5\xb4\xed\x02\xc8\x38\xca\x64\x7a\x86\xe0\x03\x81\xd1\xcf\x47\xab\x70\x8e\x8e\xe5\x44\xe5\x54\x5c\xdd\x82\x01\x03\x3c\xd2\x21\xcf\xe8\x04\xf2\x10\x14\x30\x18\xae\x30\x5e\x7a\x58\xc0\xa1\x5e\xe8\x13\x6d\x7a\xca\x91\xa2\x50\x3e\xd8\xc6\xee\x76\x07\x5c\xd9\xda\xa9\x9c\x97\x39\x9a\xd4\xc2\x94\x3c\x8f\x32\xd1\x8e\x47\x43\x77\x14\xe5\x4b\x58\xfc\x56\x1f\x1a\xec\x80\x5f\x75\xde\x42\x2f\xe4\x10\x60\x17\xbc\x00\xbd\xfe\x6e\x29\x57\x78\x1f\x4f\xde\x84\x8b\xf1\x19\x3b\x25\x5f\xd9\x31\xf2\x0f\xfa\x83\xe9\xdd\xed\x2d\x3e\x99\xc7\x5c\x91\xbf\xaf\xd2\xc6\x09\x83\xee\x60\x23\x9c\x70\xb0\x8c\x11\x0e\x2c\xfb\xa5\xce\xa5\x97\xc9\x64\xac\xf7\x53\xdb\xa6\x74\xf0\x78\x13\x4a\x53\x5b\x2a\x2c\x86\xdd\x17\xdb\x3d\xfc\xcb\xae\x1b\xfe\xf6\xe9\x6c\x3e\xdf\xfb\x34\x3f\xf7\x16\xdf\x7a\xfe\x9b\xcb\xdd\x9f\x17\x77\x17\xdb\x4a\x34\x4c\x68\x48\xdc\x8a\xcd\xff\xe9\xdd\xc1\xb4\x3f\xdd\xff\xe9\xda\xfd\xf0\xcb\x07\xd8\xbf\xe5\x3f\x1d\xf6\x6f\x7f\x3d\xd9\x5d\xc4\x94\xc9\xdf\xdd\x6a\x15\x8d\xbd\xcd\x48\xc6\xde\x52\xc1\xd8\xb3\x90\x25\xdd\xc6\x73\xc4\xf0\x64\x01\x7e\xfe\x78\xad\x6f\x84\x1d\x82\xcb\xc8\x1b\x01\x30\x14\x33\xca\xf0\xb7\xf8\xba\xa8\x5b\x44\xea\xd1\x67\xf7\xc3\xec\x74\xf6\xc5\xff\xfd\x65\xf0\xf1\xfd\xe4\xac\xef\x5d\xa0\xdb\xc0\x1d\xfc\xe3\x24\xa6\xcf\x91\xd4\x65\xaf\x28\x99\x78\xd8\x11\x35\x68\xb5\xbb\xbf\x11\x5a\x99\x60\xec\xb4\x32\x5b\x98\x2c\xa4\x0f\x27\x69\xc9\x83\x39\x80\x9e\xb2\x8d\xd4\x19\x9a\x52\x3a\xec\xdf\x7e\xea\x7e\xc0\xa7\xb7\xdf\x6e\x7f\x7f\xf5\xed\xe3\x7b\x74\xd6\xa7\x9f\xd0\xcc\xdd\x3d\x8d\xc8\x50\xbc\x89\xd5\x36\xf5\xa3\x8d\xcc\xfc\x68\xd9\xc4\x8f\xac\x3c\x92\x5e\xa4\x8f\xb2\x83\x16\x96\x1c\x9d\x9e\xcf\x5f\x1f\x7d\x7e\xfb\xeb\xa7\xfd\x4f\xd3\xd9\xe4\xed\xd1\xf4\xcd\x25\xff\x69\x7e\xfa\x31\x99\x6b\x6d\x61\xf1\x78\x33\x36\xb5\xa0\x1a\x33\xb9\x61\x04\x48\xeb\x80\x4b\xbf\xe9\xdd\xab\xb7\xed\xd3\xdf\xdb\x47\xc3\xe8\x3a\x12\xb9\x85\xf4\xa5\x23\x69\x1b\xf4\x55\xb4\x23\xdd\x07\x03\xdc\xee\xe1\xaf\xdd\x5d\x8f\xb8\x9e\x7f\xd7\xbd\x9b\x38\x07\x1c\x0b\xb8\xc7\xbd\xcf\xf3\x43\xd3\x09\x99\x18\x3f\x99\x2f\xe9\xd0\x9b\xee\xb9\x87\x87\x77\x5d\x8f\x39\xee\x7c\x30\x3d\x80\xde\xf8\x80\x7b\x93\x29\xf9\xbc\xeb\xce\xc6\xfc\xf3\x5f\xfe\xdf\x5f\x4f\x7f\xbf\xbe\x3c\x06\xff\xa5\x67\xdc\x51\x18\xbf\xc0\x2e\x22\x42\xae\x99\x19\x21\xc0\x1c\x6c\x0f\xba\x83\xed\xe7\x8a\x16\xea\xcf\x57\xe7\x1f\xae\xae\x4f\x2f\xaf\x34\x31\xe4\x47\x95\xe9\x4e\x16\x16\xa4\x80\x54\xfb\xde\x74\x8f\xb2\xbd\xee\x1c\x87\xdd\x03\x8a\xe4\xb2\xcd\xd8\xad\xd3\xdf\x77\xa7\x13\xf1\xb9\x07\x9d\x6d\x53\xc9\x46\xc9\x63\xd5\xab\x72\x12\x86\xbc\xfd\x5b\x85\x3c\xb9\xe6\x1f\xd9\x62\x9f\xf0\xbb\x71\x9f\x5f\xf8\xaf\x3f\xef\x8d\x7f\x0f\x4e\x0e\x5e\xc1\xd6\xd6\xff\x05\x00\x00\xff\xff\x91\x27\x31\x4c\x3f\xdb\x00\x00") func connector_mgmtYamlBytes() ([]byte, error) { return bindataRead( @@ -93,7 +93,7 @@ func connector_mgmtYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "connector_mgmt.yaml", size: 54112, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + info := bindataFileInfo{name: "connector_mgmt.yaml", size: 56127, mode: os.FileMode(420), modTime: time.Unix(1, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/internal/connector/internal/handlers/connector_admin.go b/internal/connector/internal/handlers/connector_admin.go index e656958a3..bda954ace 100644 --- a/internal/connector/internal/handlers/connector_admin.go +++ b/internal/connector/internal/handlers/connector_admin.go @@ -184,6 +184,42 @@ func (h *ConnectorAdminHandler) UpgradeConnectorsByOperator(writer http.Response handlers.Handle(writer, request, &cfg, http.StatusNoContent) } +func (h *ConnectorAdminHandler) GetClusterNamespaces(writer http.ResponseWriter, request *http.Request) { + id := mux.Vars(request)["connector_cluster_id"] + listArgs := coreservices.NewListArguments(request.URL.Query()) + cfg := handlers.HandlerConfig{ + Validate: []handlers.Validate{ + handlers.Validation("connector_cluster_id", &id, handlers.MinLen(1), handlers.MaxLen(maxConnectorClusterIdLength)), + }, + Action: func() (interface{}, *errors.ServiceError) { + if err := isAdmin(request); err != nil { + return nil, err + } + + namespaces, paging, err := h.NamespaceService.List(request.Context(), []string{id}, listArgs) + if err != nil { + return nil, err + } + + result := private.ConnectorNamespaceList{ + Kind: "ConnectorNamespaceList", + Page: int32(paging.Page), + Size: int32(paging.Size), + Total: int32(paging.Total), + } + + result.Items = make([]private.ConnectorNamespace, len(namespaces)) + for i, namespace := range namespaces { + result.Items[i] = presenters.PresentPrivateConnectorNamespace(namespace) + } + + return result, nil + }, + } + + handlers.HandleGet(writer, request, &cfg) +} + func (h *ConnectorAdminHandler) GetConnectorNamespaces(writer http.ResponseWriter, request *http.Request) { listArgs := coreservices.NewListArguments(request.URL.Query()) cfg := handlers.HandlerConfig{ @@ -226,7 +262,10 @@ func (h *ConnectorAdminHandler) CreateConnectorNamespace(writer http.ResponseWri } ctx := request.Context() - connectorNamespace := presenters.ConvertConnectorNamespaceWithTenantRequest(&resource) + connectorNamespace, serviceError := presenters.ConvertConnectorNamespaceWithTenantRequest(&resource) + if serviceError != nil { + return nil, serviceError + } if connectorNamespace.TenantUser != nil { connectorNamespace.Owner = connectorNamespace.TenantUser.ID } else { diff --git a/internal/connector/internal/handlers/connector_cluster.go b/internal/connector/internal/handlers/connector_cluster.go index c306d0d10..5bdd5d788 100644 --- a/internal/connector/internal/handlers/connector_cluster.go +++ b/internal/connector/internal/handlers/connector_cluster.go @@ -246,3 +246,36 @@ func (o *ConnectorClusterHandler) buildTokenURL(serviceAccount *api.ServiceAccou u.User = url.UserPassword(serviceAccount.ClientID, serviceAccount.ClientSecret) return u.String(), nil } + +func (h *ConnectorClusterHandler) GetNamespaces(writer http.ResponseWriter, request *http.Request) { + connectorClusterId := mux.Vars(request)["connector_cluster_id"] + cfg := &handlers.HandlerConfig{ + Validate: []handlers.Validate{ + handlers.Validation("connector_cluster_id", &connectorClusterId, handlers.MinLen(1), handlers.MaxLen(maxConnectorClusterIdLength)), + }, + Action: func() (interface{}, *errors.ServiceError) { + ctx := request.Context() + listArgs := coreservices.NewListArguments(request.URL.Query()) + resources, paging, err := h.ConnectorNamespace.List(ctx, []string{connectorClusterId} ,listArgs) + if err != nil { + return nil, err + } + + resourceList := public.ConnectorNamespaceList{ + Kind: "ConnectorNamespaceList", + Page: int32(paging.Page), + Size: int32(paging.Size), + Total: int32(paging.Total), + } + + for _, resource := range resources { + converted := presenters.PresentConnectorNamespace(resource) + resourceList.Items = append(resourceList.Items, converted) + } + + return resourceList, nil + }, + } + + handlers.HandleList(writer, request, cfg) +} diff --git a/internal/connector/internal/handlers/connector_namespace.go b/internal/connector/internal/handlers/connector_namespace.go index b7ab86045..4f6bbf2d4 100644 --- a/internal/connector/internal/handlers/connector_namespace.go +++ b/internal/connector/internal/handlers/connector_namespace.go @@ -20,8 +20,6 @@ import ( var ( maxConnectorNamespaceIdLength = 32 - maxConnectorNamespaceNameLength = 63 - defaultNamespaceName = "default_namespace" ) type ConnectorNamespaceHandler struct { @@ -39,30 +37,26 @@ func (h *ConnectorNamespaceHandler) Create(w http.ResponseWriter, r *http.Reques cfg := &handlers.HandlerConfig{ MarshalInto: &resource, Validate: []handlers.Validate{ - handlers.Validation("name", &resource.Name, handlers.WithDefault(defaultNamespaceName), - handlers.MinLen(1), handlers.MaxLen(maxConnectorNamespaceNameLength)), + handlers.Validation("name", &resource.Name, handlers.MinLen(1)), + handlers.Validation("cluster_id", &resource.ClusterId, handlers.MinLen(1), handlers.MaxLen(maxConnectorClusterIdLength)), + handlers.Validation("kind", &resource.Kind, handlers.IsOneOf(presenters.NamespaceKinds...)), }, Action: func() (interface{}, *errors.ServiceError) { - convResource := presenters.ConvertConnectorNamespaceRequest(&resource) - ctx := r.Context() claims, err := auth.GetClaimsFromContext(ctx) if err != nil { return nil, errors.Unauthenticated("user not authenticated") } userID := auth.GetUsernameFromClaims(claims) - convResource.Owner = userID organisationId := auth.GetOrgIdFromClaims(claims) - if convResource.ClusterId == "" { - if err := h.Service.SetTenantClusterId(convResource, organisationId); err != nil { - return nil, err - } - } else { - // TODO move auth checks in an orthogonal feature - if err := h.checkAuthorizedAccess(userID, convResource, organisationId); err != nil { - return nil, err - } + + convResource, serr := presenters.ConvertConnectorNamespaceRequest(&resource, userID, organisationId) + if serr != nil { + return nil, serr + } + if err := h.checkAuthorizedAccess(userID, convResource, organisationId); err != nil { + return nil, err } // set tenant to org if there is one, or set it to user @@ -133,30 +127,21 @@ func (h *ConnectorNamespaceHandler) CreateEvaluation(w http.ResponseWriter, r *h cfg := &handlers.HandlerConfig{ MarshalInto: &resource, Validate: []handlers.Validate{ - handlers.Validation("name", &resource.Name, handlers.WithDefault(defaultNamespaceName), - handlers.MinLen(1), handlers.MaxLen(maxConnectorNamespaceNameLength)), + handlers.Validation("name", &resource.Name, handlers.MinLen(1)), }, Action: func() (interface{}, *errors.ServiceError) { - convResource := presenters.ConvertConnectorNamespaceEvalRequest(&resource) - claims, err := auth.GetClaimsFromContext(r.Context()) if err != nil { return nil, errors.Unauthenticated("user not authenticated") } userId := auth.GetUsernameFromClaims(claims) - convResource.Owner = userId + + convResource := presenters.ConvertConnectorNamespaceEvalRequest(&resource, userId) if err := h.Service.SetEvalClusterId(convResource); err != nil { return nil, err } - // set tenant user for eval namespace - convResource.TenantUserId = &convResource.Owner - convResource.TenantUser = &dbapi.ConnectorTenantUser{ - Model: db.Model{ - ID: userId, - }, - } if err := h.Service.Create(r.Context(), convResource); err != nil { return nil, err } @@ -237,7 +222,6 @@ func (h *ConnectorNamespaceHandler) List(w http.ResponseWriter, r *http.Request) if err != nil { return nil, errors.Unauthenticated("user not authenticated") } - // TODO handle creating default namespaces in List() userId := auth.GetUsernameFromClaims(claims) var ownerClusterIds []string if err := h.Service.GetOwnerClusterIds(userId, &ownerClusterIds); err != nil { diff --git a/internal/connector/internal/presenters/connector_namespace.go b/internal/connector/internal/presenters/connector_namespace.go index 8325f0eed..342c1e8cc 100644 --- a/internal/connector/internal/presenters/connector_namespace.go +++ b/internal/connector/internal/presenters/connector_namespace.go @@ -7,6 +7,7 @@ import ( "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/public" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/api" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/db" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/errors" "strings" "time" ) @@ -16,13 +17,17 @@ const ( OrganisationKind string = "organisation" ) -func ConvertConnectorNamespaceRequest(namespaceRequest *public.ConnectorNamespaceRequest) *dbapi.ConnectorNamespace { +var NamespaceKinds = []string{UserKind, OrganisationKind} + +func ConvertConnectorNamespaceRequest(namespaceRequest *public.ConnectorNamespaceRequest, + userID string, organisationID string) (*dbapi.ConnectorNamespace, *errors.ServiceError) { result := &dbapi.ConnectorNamespace{ Model: db.Model{ ID: api.NewID(), }, Name: namespaceRequest.Name, ClusterId: namespaceRequest.ClusterId, + Owner: userID, } result.Annotations = make([]dbapi.ConnectorNamespaceAnnotation, len(namespaceRequest.Annotations)) for i, annotation := range namespaceRequest.Annotations { @@ -30,16 +35,38 @@ func ConvertConnectorNamespaceRequest(namespaceRequest *public.ConnectorNamespac result.Annotations[i].Name = annotation.Name result.Annotations[i].Value = annotation.Value } + switch namespaceRequest.Kind { + case UserKind: + result.TenantUserId = &userID + result.TenantUser = &dbapi.ConnectorTenantUser{ + Model: db.Model{ + ID: userID, + }, + } + case OrganisationKind: + if organisationID == "" { + return nil, errors.BadRequest("missing organization for tenant organisation namespace") + } + result.TenantOrganisationId = &organisationID + result.TenantOrganisation = &dbapi.ConnectorTenantOrganisation{ + Model: db.Model{ + ID: organisationID, + }, + } + default: + return nil, errors.BadRequest("invalid tenant kind: %s", namespaceRequest.Kind) + } - return result + return result, nil } -func ConvertConnectorNamespaceEvalRequest(namespaceRequest *public.ConnectorNamespaceEvalRequest) *dbapi.ConnectorNamespace { +func ConvertConnectorNamespaceEvalRequest(namespaceRequest *public.ConnectorNamespaceEvalRequest, userID string) *dbapi.ConnectorNamespace { result := &dbapi.ConnectorNamespace{ Model: db.Model{ ID: api.NewID(), }, - Name: namespaceRequest.Name, + Name: namespaceRequest.Name, + Owner: userID, } result.Annotations = make([]dbapi.ConnectorNamespaceAnnotation, len(namespaceRequest.Annotations)) for i, annotation := range namespaceRequest.Annotations { @@ -47,11 +74,17 @@ func ConvertConnectorNamespaceEvalRequest(namespaceRequest *public.ConnectorName result.Annotations[i].Name = annotation.Name result.Annotations[i].Value = annotation.Value } + result.TenantUserId = &result.Owner + result.TenantUser = &dbapi.ConnectorTenantUser{ + Model: db.Model{ + ID: userID, + }, + } return result } -func ConvertConnectorNamespaceWithTenantRequest(namespaceRequest *private.ConnectorNamespaceWithTenantRequest) *dbapi.ConnectorNamespace { +func ConvertConnectorNamespaceWithTenantRequest(namespaceRequest *private.ConnectorNamespaceWithTenantRequest) (*dbapi.ConnectorNamespace, *errors.ServiceError) { result := &dbapi.ConnectorNamespace{ Model: db.Model{ ID: api.NewID(), @@ -75,7 +108,7 @@ func ConvertConnectorNamespaceWithTenantRequest(namespaceRequest *private.Connec }, } default: - // ignore, should have been validated earlier + return nil, errors.BadRequest("invalid kind %s", namespaceRequest.Tenant.Kind) } result.Annotations = make([]dbapi.ConnectorNamespaceAnnotation, len(namespaceRequest.Annotations)) for i, annotation := range namespaceRequest.Annotations { @@ -84,7 +117,7 @@ func ConvertConnectorNamespaceWithTenantRequest(namespaceRequest *private.Connec result.Annotations[i].Value = annotation.Value } - return result + return result, nil } func PresentConnectorNamespace(namespace *dbapi.ConnectorNamespace) public.ConnectorNamespace { diff --git a/internal/connector/internal/routes/route_loader.go b/internal/connector/internal/routes/route_loader.go index e2462d4eb..38df6f00f 100644 --- a/internal/connector/internal/routes/route_loader.go +++ b/internal/connector/internal/routes/route_loader.go @@ -110,6 +110,7 @@ func (s *options) AddRoutes(mainRouter *mux.Router) error { apiV1ConnectorClustersRouter.HandleFunc("/{connector_cluster_id}", s.ConnectorClusterHandler.Update).Methods(http.MethodPut) apiV1ConnectorClustersRouter.HandleFunc("/{connector_cluster_id}", s.ConnectorClusterHandler.Delete).Methods(http.MethodDelete) apiV1ConnectorClustersRouter.HandleFunc("/{connector_cluster_id}/{_:addon[-_]parameters}", s.ConnectorClusterHandler.GetAddonParameters).Methods(http.MethodGet) + apiV1ConnectorClustersRouter.HandleFunc("/{connector_cluster_id}/namespaces", s.ConnectorClusterHandler.GetNamespaces).Methods(http.MethodGet) apiV1ConnectorClustersRouter.Use(s.AuthorizeMiddleware.Authorize) // /api/connector_mgmt/v1/kafka_connector_namespaces @@ -150,6 +151,7 @@ func (s *options) AddRoutes(mainRouter *mux.Router) error { adminRouter.Use(auth.NewRolesAuhzMiddleware().RequireRolesForMethods(rolesMapping, kerrors.ErrorNotFound)) adminRouter.Use(auth.NewAuditLogMiddleware().AuditLog(kerrors.ErrorNotFound)) adminRouter.HandleFunc("/{_:kafka[-_]connector[-_]clusters}", s.ConnectorAdminHandler.ListConnectorClusters).Methods(http.MethodGet) + adminRouter.HandleFunc("/{_:kafka[-_]connector[-_]clusters}/{connector_cluster_id}/namespaces", s.ConnectorAdminHandler.GetClusterNamespaces).Methods(http.MethodGet) adminRouter.HandleFunc("/{_:kafka[-_]connector[-_]clusters}/{connector_cluster_id}/upgrades/type", s.ConnectorAdminHandler.GetConnectorUpgradesByType).Methods(http.MethodGet) adminRouter.HandleFunc("/{_:kafka[-_]connector[-_]clusters}/{connector_cluster_id}/upgrades/type", s.ConnectorAdminHandler.UpgradeConnectorsByType).Methods(http.MethodPut) adminRouter.HandleFunc("/{_:kafka[-_]connector[-_]clusters}/{connector_cluster_id}/upgrades/operator", s.ConnectorAdminHandler.GetConnectorUpgradesByOperator).Methods(http.MethodGet) diff --git a/internal/connector/internal/services/connector_namespaces.go b/internal/connector/internal/services/connector_namespaces.go index fa06ec762..054f132db 100644 --- a/internal/connector/internal/services/connector_namespaces.go +++ b/internal/connector/internal/services/connector_namespaces.go @@ -22,7 +22,6 @@ type ConnectorNamespaceService interface { Get(ctx context.Context, namespaceID string) (*dbapi.ConnectorNamespace, *errors.ServiceError) List(ctx context.Context, clusterIDs []string, listArguments *services.ListArguments) (dbapi.ConnectorNamespaceList, *api.PagingMeta, *errors.ServiceError) Delete(ctx context.Context, namespaceName string) *errors.ServiceError - SetTenantClusterId(request *dbapi.ConnectorNamespace, organisationId string) *errors.ServiceError SetEvalClusterId(request *dbapi.ConnectorNamespace) *errors.ServiceError GetOwnerClusterIds(userId string, ownerClusterIds *[]string) *errors.ServiceError GetOrgClusterIds(userId string, organisationId string, orgClusterIds *[]string) *errors.ServiceError @@ -50,46 +49,6 @@ func NewConnectorNamespaceService(factory *db.ConnectionFactory, config *config. } } -func (k *connectorNamespaceService) SetTenantClusterId(request *dbapi.ConnectorNamespace, organisationId string) *errors.ServiceError { - userID := request.Owner - - // get owned clusters - var ownerClusterIds []string - if err := k.GetOwnerClusterIds(userID, &ownerClusterIds); err != nil { - return err - } - - numOwnerClusters := len(ownerClusterIds) - if numOwnerClusters == 0 { - - // get org clusters - var orgClusterIds []string - if err := k.GetOrgClusterIds(userID, organisationId, &orgClusterIds); err != nil { - return err - } - - numOrgClusters := len(orgClusterIds) - if numOrgClusters == 0 { - return errors.Unauthorized("no clusters for organisation %v", organisationId) - } else if numOrgClusters == 1 { - request.ClusterId = orgClusterIds[0] - } else { - // TODO add support for load balancing strategies - // pick a cluster at random - request.ClusterId = orgClusterIds[rand.Intn(numOrgClusters)] - } - - } else if numOwnerClusters == 1 { - request.ClusterId = ownerClusterIds[0] - } else { - // TODO add support for load balancing strategies - // pick a cluster at random - request.ClusterId = ownerClusterIds[rand.Intn(numOwnerClusters)] - } - - return nil -} - func (k *connectorNamespaceService) GetOrgClusterIds(userId, organisationId string, orgClusterIds *[]string) *errors.ServiceError { dbConn := k.connectionFactory.New() if err := dbConn.Raw("SELECT id FROM connector_clusters WHERE deleted_at is null AND owner <> ? AND organisation_id = ?", userId, organisationId). @@ -220,6 +179,16 @@ func (k *connectorNamespaceService) List(ctx context.Context, clusterIDs []strin } dbConn = dbConn.Offset((pagingMeta.Page - 1) * pagingMeta.Size).Limit(pagingMeta.Size) + if len(listArguments.OrderBy) == 0 { + // default orderBy name + dbConn = dbConn.Order("name ASC") + } + + // Set the order by arguments if any + for _, orderByArg := range listArguments.OrderBy { + dbConn = dbConn.Order(orderByArg) + } + // execute query result := dbConn. Preload("Annotations"). @@ -284,7 +253,15 @@ func (k *connectorNamespaceService) Delete(ctx context.Context, namespaceId stri const defaultNamespaceName = "default-connector-namespace" func (k *connectorNamespaceService) CreateDefaultNamespace(ctx context.Context, connectorCluster *dbapi.ConnectorCluster) *errors.ServiceError { - namespaceRequest := presenters.ConvertConnectorNamespaceRequest(&public.ConnectorNamespaceRequest{ + owner := connectorCluster.Owner + organisationId := connectorCluster.OrganisationId + + kind := presenters.UserKind + if organisationId != "" { + kind = presenters.OrganisationKind + } + + namespaceRequest, err := presenters.ConvertConnectorNamespaceRequest(&public.ConnectorNamespaceRequest{ Name: defaultNamespaceName, Annotations: []public.ConnectorNamespaceRequestMetaAnnotations{ { @@ -293,26 +270,11 @@ func (k *connectorNamespaceService) CreateDefaultNamespace(ctx context.Context, }, }, ClusterId: connectorCluster.ID, - }) + Kind: kind, + }, owner, organisationId) - // namespace has the same owner and tenant org as cluster - owner := connectorCluster.Owner - namespaceRequest.Owner = owner - organisationId := connectorCluster.OrganisationId - if organisationId != "" { - namespaceRequest.TenantOrganisationId = &organisationId - namespaceRequest.TenantOrganisation = &dbapi.ConnectorTenantOrganisation{ - Model: db.Model{ - ID: organisationId, - }, - } - } else { - namespaceRequest.TenantUserId = &owner - namespaceRequest.TenantUser = &dbapi.ConnectorTenantUser{ - Model: db.Model{ - ID: owner, - }, - } + if err != nil { + return err } return k.Create(ctx, namespaceRequest) } diff --git a/internal/connector/test/integration/features/connector-agent-api.feature b/internal/connector/test/integration/features/connector-agent-api.feature index ab00d2c77..850c93fe9 100644 --- a/internal/connector/test/integration/features/connector-agent-api.feature +++ b/internal/connector/test/integration/features/connector-agent-api.feature @@ -28,7 +28,7 @@ Feature: connector agent API And the ".status.state" selection from the response should match "disconnected" Given I store the ".id" selection from the response as ${connector_cluster_id} - When I GET path "/v1/kafka_connector_namespaces/?search=cluster_id=${connector_cluster_id}" + When I GET path "/v1/kafka_connector_clusters/${connector_cluster_id}/namespaces" Then the response code should be 200 Given I store the ".items[0].id" selection from the response as ${connector_namespace_id} diff --git a/internal/connector/test/integration/features/connector-multitenancy-api.feature b/internal/connector/test/integration/features/connector-multitenancy-api.feature index 978d3bbec..a4dfb466f 100644 --- a/internal/connector/test/integration/features/connector-multitenancy-api.feature +++ b/internal/connector/test/integration/features/connector-multitenancy-api.feature @@ -201,6 +201,7 @@ Feature: connector namespaces API { "name": "shared_namespace", "cluster_id": "${connector_cluster_id}", + "kind": "organisation", "annotations": [ { "name": "connector_mgmt.api.openshift.com/profile", @@ -451,7 +452,7 @@ Feature: connector namespaces API Given I am logged in as "Ricky Bobby" When I DELETE path "/v1/admin/kafka_connector_namespaces/${namespace_id}" Then the response code should be 204 - And I GET path "/v1/admin/kafka_connector_namespaces?search=cluster_id=${connector_cluster_id}" + And I GET path "/v1/admin/kafka_connector_clusters/${connector_cluster_id}/namespaces" And the response code should be 200 And the response should match json: """ diff --git a/openapi/connector_mgmt-private-admin.yaml b/openapi/connector_mgmt-private-admin.yaml index d645f7beb..08be71bb8 100644 --- a/openapi/connector_mgmt-private-admin.yaml +++ b/openapi/connector_mgmt-private-admin.yaml @@ -61,6 +61,60 @@ paths: operationId: listConnectorClusters summary: Returns a list of connector clusters + /api/connector_mgmt/v1/admin/kafka_connector_clusters/{connector_cluster_id}/namespaces: + get: + tags: + - Connector Clusters Admin + parameters: + - name: connector_cluster_id + description: The id of the connector cluster + schema: + type: string + in: path + required: true + - $ref: "connector_mgmt.yaml#/components/parameters/page" + - $ref: "connector_mgmt.yaml#/components/parameters/size" + - $ref: "connector_mgmt.yaml#/components/parameters/orderBy" + - $ref: "connector_mgmt.yaml#/components/parameters/search" + responses: + "200": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/ConnectorNamespaceList" + description: Connector namespaces + "401": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/Error" + examples: + 401Example: + $ref: "connector_mgmt.yaml#/components/examples/401Example" + description: Auth token is invalid + "404": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/Error" + examples: + 404Example: + $ref: "connector_mgmt.yaml#/components/examples/404Example" + description: No matching connector namespace exists + "500": + content: + application/json: + schema: + $ref: "connector_mgmt.yaml#/components/schemas/Error" + examples: + 500Example: + $ref: "connector_mgmt.yaml#/components/examples/500Example" + description: Unexpected error occurred + security: + - Bearer: [ ] + operationId: getClusterNamespaces + summary: Get a list of available connector namespaces in cluster + /api/connector_mgmt/v1/admin/kafka_connector_clusters/{connector_cluster_id}/upgrades/type: parameters: - name: connector_cluster_id @@ -471,10 +525,16 @@ components: type: string ConnectorNamespaceWithTenantRequest: + required: + - name + - cluster_id + - tenant allOf: - - $ref: "connector_mgmt.yaml#/components/schemas/ConnectorNamespaceRequest" + - $ref: "connector_mgmt.yaml#/components/schemas/ConnectorNamespaceEvalRequest" - type: object properties: + cluster_id: + type: string tenant: $ref: "connector_mgmt.yaml#/components/schemas/ConnectorNamespaceTenant" diff --git a/openapi/connector_mgmt.yaml b/openapi/connector_mgmt.yaml index ef3c30e30..4c1e3f511 100644 --- a/openapi/connector_mgmt.yaml +++ b/openapi/connector_mgmt.yaml @@ -697,6 +697,61 @@ paths: $ref: "#/components/examples/500Example" description: Unexpected error occurred + "/api/connector_mgmt/v1/kafka_connector_clusters/{connector_cluster_id}/namespaces": + parameters: + - name: connector_cluster_id + description: The id of the connector cluster + schema: + type: string + in: path + required: true + - $ref: "#/components/parameters/page" + - $ref: "#/components/parameters/size" + - $ref: "#/components/parameters/orderBy" + - $ref: "#/components/parameters/search" + get: + tags: + - Connector Clusters + security: + - Bearer: [ ] + operationId: getConnectorClusterNamespaces + summary: Get a connector cluster's namespaces + description: Get a connector cluster's namespaces + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/ConnectorNamespaceList" + description: The namespaces visible to user in the cluster. + "401": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 401Example: + $ref: "#/components/examples/401Example" + description: Auth token is invalid + "404": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 404Example: + $ref: "#/components/examples/404Example" + description: No matching connector cluster type exists + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + 500Example: + $ref: "#/components/examples/500Example" + description: Unexpected error occurred + # # Connector Namespaces # @@ -1432,14 +1487,19 @@ components: ConnectorNamespaceRequest: description: A connector namespace create request + required: + - name + - cluster_id + - kind allOf: - $ref: "#/components/schemas/ConnectorNamespaceEvalRequest" - type: object properties: cluster_id: type: string - required: - - name + kind: + type: string + description: One of 'user' or 'organisation' similar to 'ConnectorNamespaceTenant' ConnectorNamespaceEvalRequest: description: An evaluation connector namespace create request From be15bc9d746b2f02d14a6c3da7c53e7bfdb1e7d5 Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Mon, 28 Feb 2022 16:41:02 -0800 Subject: [PATCH 04/12] fix(connectors): fix missing deleted_at indices --- .../202202280000_add_deleted_at_index.go | 28 +++++++++++++++++++ .../internal/migrations/migrations.go | 1 + 2 files changed, 29 insertions(+) create mode 100644 internal/connector/internal/migrations/202202280000_add_deleted_at_index.go diff --git a/internal/connector/internal/migrations/202202280000_add_deleted_at_index.go b/internal/connector/internal/migrations/202202280000_add_deleted_at_index.go new file mode 100644 index 000000000..875656ee6 --- /dev/null +++ b/internal/connector/internal/migrations/202202280000_add_deleted_at_index.go @@ -0,0 +1,28 @@ +package migrations + +// Migrations should NEVER use types from other packages. Types can change +// and then migrations run on a _new_ database will fail or behave unexpectedly. +// Instead of importing types, always re-create the type in the migration, as +// is done here, even though the same type is defined in pkg/api + +import ( + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/db" + "github.com/go-gormigrate/gormigrate/v2" +) + +func addDeletedAtIndex(migrationId string) *gormigrate.Migration { + + // add missing deleted_at index caused by incorrectly using api.Meta instead of db.Model in dbapi.* structs + return db.CreateMigrationFromActions(migrationId, + db.ExecAction(`CREATE INDEX on connectors(deleted_at)`, + `DROP INDEX IF EXISTS connectors_deleted_at_idx`), + db.ExecAction(`CREATE INDEX on connector_statuses(deleted_at)`, + `DROP INDEX IF EXISTS connector_statuses_deleted_at_idx`), + db.ExecAction(`CREATE INDEX on connector_channels(deleted_at)`, + `DROP INDEX IF EXISTS connector_channels_deleted_at_idx`), + db.ExecAction(`CREATE INDEX on connector_types(deleted_at)`, + `DROP INDEX IF EXISTS connector_types_deleted_at_idx`), + db.ExecAction(`CREATE INDEX on connector_clusters(deleted_at)`, + `DROP INDEX IF EXISTS connector_clusters_deleted_at_idx`), + ) +} diff --git a/internal/connector/internal/migrations/migrations.go b/internal/connector/internal/migrations/migrations.go index 842295319..023778d9d 100644 --- a/internal/connector/internal/migrations/migrations.go +++ b/internal/connector/internal/migrations/migrations.go @@ -30,6 +30,7 @@ var migrations = []*gormigrate.Migration{ addClientId("202202030000"), addConnectorNamespaceTables("202202070000"), addConnectorNamespaceDeployment("202202220000"), + addDeletedAtIndex("202202280000"), } func New(dbConfig *db.DatabaseConfig) (*db.Migration, func(), error) { From dc34871feabc303356b375dd317f94041db80b99 Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Tue, 1 Mar 2022 22:00:47 -0800 Subject: [PATCH 05/12] fix(connectors): replace namespace name with namespace ID in admin upgrades API --- .../api/admin/private/api/openapi.yaml | 8 ++++---- ...del_connector_available_operator_upgrade.go | 2 +- .../model_connector_available_type_upgrade.go | 2 +- .../connector/internal/api/dbapi/connector.go | 4 ++-- .../connector_available_operator_upgrade.go | 4 ++-- .../connector_available_type_upgrade.go | 4 ++-- .../internal/services/connector_cluster.go | 18 ++++++++---------- .../features/connector-agent-api.feature | 4 ++-- openapi/connector_mgmt-private-admin.yaml | 4 ++-- 9 files changed, 24 insertions(+), 26 deletions(-) diff --git a/internal/connector/internal/api/admin/private/api/openapi.yaml b/internal/connector/internal/api/admin/private/api/openapi.yaml index 6d7156719..3ed6a1ac1 100644 --- a/internal/connector/internal/api/admin/private/api/openapi.yaml +++ b/internal/connector/internal/api/admin/private/api/openapi.yaml @@ -807,17 +807,17 @@ components: ConnectorAvailableTypeUpgrade: description: An available type upgrade for a connector example: + namespace_id: namespace_id shard_metadata: available_id: 6 assigned_id: 0 connector_id: connector_id - namespace: namespace channel: channel connector_type_id: connector_type_id properties: connector_id: type: string - namespace: + namespace_id: type: string connector_type_id: type: string @@ -833,8 +833,8 @@ components: ConnectorAvailableOperatorUpgrade: description: An available operator upgrade for a connector example: + namespace_id: namespace_id connector_id: connector_id - namespace: namespace channel: channel operator: available_id: available_id @@ -843,7 +843,7 @@ components: properties: connector_id: type: string - namespace: + namespace_id: type: string connector_type_id: type: string diff --git a/internal/connector/internal/api/admin/private/model_connector_available_operator_upgrade.go b/internal/connector/internal/api/admin/private/model_connector_available_operator_upgrade.go index a8bc5f20a..f41298df2 100644 --- a/internal/connector/internal/api/admin/private/model_connector_available_operator_upgrade.go +++ b/internal/connector/internal/api/admin/private/model_connector_available_operator_upgrade.go @@ -12,7 +12,7 @@ package private // ConnectorAvailableOperatorUpgrade An available operator upgrade for a connector type ConnectorAvailableOperatorUpgrade struct { ConnectorId string `json:"connector_id,omitempty"` - Namespace string `json:"namespace,omitempty"` + NamespaceId string `json:"namespace_id,omitempty"` ConnectorTypeId string `json:"connector_type_id,omitempty"` Channel string `json:"channel,omitempty"` Operator ConnectorAvailableOperatorUpgradeOperator `json:"operator,omitempty"` diff --git a/internal/connector/internal/api/admin/private/model_connector_available_type_upgrade.go b/internal/connector/internal/api/admin/private/model_connector_available_type_upgrade.go index eb13a8507..623475179 100644 --- a/internal/connector/internal/api/admin/private/model_connector_available_type_upgrade.go +++ b/internal/connector/internal/api/admin/private/model_connector_available_type_upgrade.go @@ -12,7 +12,7 @@ package private // ConnectorAvailableTypeUpgrade An available type upgrade for a connector type ConnectorAvailableTypeUpgrade struct { ConnectorId string `json:"connector_id,omitempty"` - Namespace string `json:"namespace,omitempty"` + NamespaceId string `json:"namespace_id,omitempty"` ConnectorTypeId string `json:"connector_type_id,omitempty"` Channel string `json:"channel,omitempty"` ShardMetadata ConnectorAvailableTypeUpgradeShardMetadata `json:"shard_metadata,omitempty"` diff --git a/internal/connector/internal/api/dbapi/connector.go b/internal/connector/internal/api/dbapi/connector.go index ff723997a..c2d925313 100644 --- a/internal/connector/internal/api/dbapi/connector.go +++ b/internal/connector/internal/api/dbapi/connector.go @@ -123,7 +123,7 @@ type ConnectorDeploymentTypeUpgrade struct { ConnectorID string `json:"connector_id,omitempty"` DeploymentID string `json:"deployment_id,omitempty"` ConnectorTypeId string `json:"connector_type_id,omitempty"` - Namespace string `json:"namespace,omitempty"` + NamespaceID string `json:"namespace_id,omitempty"` Channel string `json:"channel,omitempty"` ShardMetadata *ConnectorTypeUpgrade `json:"shard_metadata,omitempty"` } @@ -139,7 +139,7 @@ type ConnectorDeploymentOperatorUpgrade struct { ConnectorID string `json:"connector_id,omitempty"` DeploymentID string `json:"deployment_id,omitempty"` ConnectorTypeId string `json:"connector_type_id,omitempty"` - Namespace string `json:"namespace,omitempty"` + NamespaceID string `json:"namespace_id,omitempty"` Channel string `json:"channel,omitempty"` Operator *ConnectorOperatorUpgrade `json:"operator,omitempty"` } diff --git a/internal/connector/internal/presenters/connector_available_operator_upgrade.go b/internal/connector/internal/presenters/connector_available_operator_upgrade.go index ceb4a00b5..ddf7027b4 100644 --- a/internal/connector/internal/presenters/connector_available_operator_upgrade.go +++ b/internal/connector/internal/presenters/connector_available_operator_upgrade.go @@ -9,7 +9,7 @@ func PresentConnectorAvailableOperatorUpgrade(req *dbapi.ConnectorDeploymentOper return &private.ConnectorAvailableOperatorUpgrade{ ConnectorId: req.ConnectorID, ConnectorTypeId: req.ConnectorTypeId, - Namespace: req.Namespace, + NamespaceId: req.NamespaceID, Channel: req.Channel, Operator: private.ConnectorAvailableOperatorUpgradeOperator{ AssignedId: req.Operator.Assigned.Id, @@ -22,7 +22,7 @@ func ConvertConnectorAvailableOperatorUpgrade(req *private.ConnectorAvailableOpe return &dbapi.ConnectorDeploymentOperatorUpgrade{ ConnectorID: req.ConnectorId, ConnectorTypeId: req.ConnectorTypeId, - Namespace: req.Namespace, + NamespaceID: req.NamespaceId, Channel: req.Channel, Operator: &dbapi.ConnectorOperatorUpgrade{ Assigned: dbapi.ConnectorOperator{ diff --git a/internal/connector/internal/presenters/connector_available_type_upgrade.go b/internal/connector/internal/presenters/connector_available_type_upgrade.go index 12f5ac058..b39d7f5db 100644 --- a/internal/connector/internal/presenters/connector_available_type_upgrade.go +++ b/internal/connector/internal/presenters/connector_available_type_upgrade.go @@ -9,7 +9,7 @@ func PresentConnectorAvailableTypeUpgrade(req *dbapi.ConnectorDeploymentTypeUpgr return &private.ConnectorAvailableTypeUpgrade{ ConnectorId: req.ConnectorID, ConnectorTypeId: req.ConnectorTypeId, - Namespace: req.Namespace, + NamespaceId: req.NamespaceID, Channel: req.Channel, ShardMetadata: private.ConnectorAvailableTypeUpgradeShardMetadata{ AssignedId: req.ShardMetadata.AssignedId, @@ -22,7 +22,7 @@ func ConvertConnectorAvailableTypeUpgrade(req *private.ConnectorAvailableTypeUpg return &dbapi.ConnectorDeploymentTypeUpgrade{ ConnectorID: req.ConnectorId, ConnectorTypeId: req.ConnectorTypeId, - Namespace: req.Namespace, + NamespaceID: req.NamespaceId, Channel: req.Channel, ShardMetadata: &dbapi.ConnectorTypeUpgrade{ AssignedId: req.ShardMetadata.AssignedId, diff --git a/internal/connector/internal/services/connector_cluster.go b/internal/connector/internal/services/connector_cluster.go index 54b061bb8..edb3305cf 100644 --- a/internal/connector/internal/services/connector_cluster.go +++ b/internal/connector/internal/services/connector_cluster.go @@ -756,7 +756,7 @@ func (k *connectorClusterService) GetAvailableDeploymentTypeUpgrades(listArgs *s ConnectorTypeUpgradeFrom int64 ConnectorTypeUpgradeTo int64 ConnectorTypeID string - Namespace string + NamespaceID string Channel string } @@ -766,15 +766,14 @@ func (k *connectorClusterService) GetAvailableDeploymentTypeUpgrades(listArgs *s dbConn = dbConn.Select( "connector_deployments.connector_id AS connector_id", "connector_deployments.id AS deployment_id", + "connector_deployments.namespace_id AS namespace_id", "connector_shard_metadata.id AS connector_type_upgrade_from", "connector_shard_metadata.latest_id AS connector_type_upgrade_to", "connector_shard_metadata.connector_type_id", - "connector_namespaces.name AS namespace", "connector_shard_metadata.channel", ) dbConn = dbConn.Joins("LEFT JOIN connector_shard_metadata ON connector_shard_metadata.id = connector_deployments.connector_type_channel_id") dbConn = dbConn.Joins("LEFT JOIN connector_deployment_statuses ON connector_deployment_statuses.id = connector_deployments.id") - dbConn = dbConn.Joins("LEFT JOIN connector_namespaces ON connector_namespaces.id = connector_deployments.namespace_id") dbConn = dbConn.Where("connector_shard_metadata.latest_id IS NOT NULL") dbConn = dbConn.Or("connector_deployment_statuses.upgrade_available") @@ -795,7 +794,7 @@ func (k *connectorClusterService) GetAvailableDeploymentTypeUpgrades(listArgs *s ConnectorID: r.ConnectorID, DeploymentID: r.DeploymentID, ConnectorTypeId: r.ConnectorTypeID, - Namespace: r.Namespace, + NamespaceID: r.NamespaceID, Channel: r.Channel, } @@ -876,9 +875,9 @@ func (k *connectorClusterService) GetAvailableDeploymentOperatorUpgrades(listArg type Result struct { ConnectorID string DeploymentID string - ConnectorTypeID string - Namespace string - Channel string + ConnectorTypeID string + NamespaceID string + Channel string ConnectorOperators api.JSON } @@ -888,14 +887,13 @@ func (k *connectorClusterService) GetAvailableDeploymentOperatorUpgrades(listArg dbConn = dbConn.Select( "connector_deployments.connector_id AS connector_id", "connector_deployments.id AS deployment_id", + "connector_deployments.namespace_id AS namespace_id", "connector_shard_metadata.connector_type_id", - "connector_namespaces.name AS namespace", "connector_shard_metadata.channel", "connector_deployment_statuses.operators AS connector_operators", ) dbConn = dbConn.Joins("LEFT JOIN connector_shard_metadata ON connector_shard_metadata.id = connector_deployments.connector_type_channel_id") dbConn = dbConn.Joins("LEFT JOIN connector_deployment_statuses ON connector_deployment_statuses.id = connector_deployments.id") - dbConn = dbConn.Joins("LEFT JOIN connector_namespaces ON connector_namespaces.id = connector_deployments.namespace_id") dbConn = dbConn.Where("connector_deployment_statuses.upgrade_available") if err := dbConn.Scan(&results).Error; err != nil { @@ -914,7 +912,7 @@ func (k *connectorClusterService) GetAvailableDeploymentOperatorUpgrades(listArg ConnectorID: r.ConnectorID, DeploymentID: r.DeploymentID, ConnectorTypeId: r.ConnectorTypeID, - Namespace: r.Namespace, + NamespaceID: r.NamespaceID, Channel: r.Channel, } diff --git a/internal/connector/test/integration/features/connector-agent-api.feature b/internal/connector/test/integration/features/connector-agent-api.feature index 850c93fe9..5a673db5d 100644 --- a/internal/connector/test/integration/features/connector-agent-api.feature +++ b/internal/connector/test/integration/features/connector-agent-api.feature @@ -690,7 +690,7 @@ Feature: connector agent API "items": [{ "connector_id": "${connector_id}", - "namespace": "default-connector-namespace", + "namespace_id": "${connector_namespace_id}", "connector_type_id": "aws-sqs-source-v1alpha1", "channel": "stable", "shard_metadata": { @@ -796,7 +796,7 @@ Feature: connector agent API "items": [{ "connector_id": "${connector_id}", - "namespace": "default-connector-namespace", + "namespace_id": "${connector_namespace_id}", "connector_type_id": "aws-sqs-source-v1alpha1", "channel": "stable", "operator": { diff --git a/openapi/connector_mgmt-private-admin.yaml b/openapi/connector_mgmt-private-admin.yaml index 08be71bb8..da93cdc61 100644 --- a/openapi/connector_mgmt-private-admin.yaml +++ b/openapi/connector_mgmt-private-admin.yaml @@ -478,7 +478,7 @@ components: properties: connector_id: type: string - namespace: + namespace_id: type: string connector_type_id: type: string @@ -510,7 +510,7 @@ components: properties: connector_id: type: string - namespace: + namespace_id: type: string connector_type_id: type: string From bda68b8cd2ffdf0cda1f66ffaa08062593c4d580 Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Wed, 2 Mar 2022 09:58:53 -0800 Subject: [PATCH 06/12] fix(connectors): fixed connector-eval-duration flag to directly use time.Duration --- .../connector/internal/config/connectors.go | 23 ++++--------------- internal/connector/providers.go | 2 +- .../test/integration/feature_test.go | 3 ++- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/internal/connector/internal/config/connectors.go b/internal/connector/internal/config/connectors.go index be0ef6449..d9d5898eb 100644 --- a/internal/connector/internal/config/connectors.go +++ b/internal/connector/internal/config/connectors.go @@ -17,17 +17,14 @@ import ( ) type ConnectorsConfig struct { - ConnectorEvalDurationString string `json:"connector_eval_duration"` - ConnectorEvalOrganizations []string `json:"connector_eval_organizations"` - ConnectorCatalogDirs []string `json:"connector_types"` - CatalogEntries []ConnectorCatalogEntry `json:"connector_type_urls"` - ConnectorEvalDuration time.Duration + ConnectorEvalDuration time.Duration `json:"connector_eval_duration"` + ConnectorEvalOrganizations []string `json:"connector_eval_organizations"` + ConnectorCatalogDirs []string `json:"connector_types"` + CatalogEntries []ConnectorCatalogEntry `json:"connector_type_urls"` } var _ environments.ConfigModule = &ConnectorsConfig{} -var _ environments.ServiceValidator = &ConnectorsConfig{} - type ConnectorChannelConfig struct { Revision int64 `json:"revision,omitempty"` ShardMetadata map[string]interface{} `json:"shard_metadata,omitempty"` @@ -44,7 +41,7 @@ func NewConnectorsConfig() *ConnectorsConfig { func (c *ConnectorsConfig) AddFlags(fs *pflag.FlagSet) { fs.StringArrayVar(&c.ConnectorCatalogDirs, "connector-catalog", c.ConnectorCatalogDirs, "Directory containing connector catalog entries") - fs.StringVar(&c.ConnectorEvalDurationString, "connector-eval-duration", c.ConnectorEvalDurationString, "Connector eval duration in golang duration format") + fs.DurationVar(&c.ConnectorEvalDuration, "connector-eval-duration", c.ConnectorEvalDuration, "Connector eval duration in golang duration format") fs.StringArrayVar(&c.ConnectorEvalOrganizations, "connector-eval-organizations", c.ConnectorEvalOrganizations, "Connector eval organization IDs") } @@ -93,13 +90,3 @@ func (c *ConnectorsConfig) ReadFiles() error { c.CatalogEntries = values return nil } - -func (c *ConnectorsConfig) Validate(env *environments.Env) error { - // validate duration - duration, err := time.ParseDuration(c.ConnectorEvalDurationString) - if err != nil { - return err - } - c.ConnectorEvalDuration = duration - return nil -} diff --git a/internal/connector/providers.go b/internal/connector/providers.go index 46fe3a098..e33c17a7c 100644 --- a/internal/connector/providers.go +++ b/internal/connector/providers.go @@ -21,7 +21,7 @@ import ( func ConfigProviders(kafkaEnabled bool) di.Option { result := di.Options( - di.Provide(config.NewConnectorsConfig, di.As(new(environments2.ConfigModule)), di.As(new(environments2.ServiceValidator))), + di.Provide(config.NewConnectorsConfig, di.As(new(environments2.ConfigModule))), di.Provide(environments2.Func(serviceProviders)), di.Provide(migrations.New), di.Provide(cmdvault.NewVaultCommand), diff --git a/internal/connector/test/integration/feature_test.go b/internal/connector/test/integration/feature_test.go index 4a3889c75..6ddaf5fc2 100644 --- a/internal/connector/test/integration/feature_test.go +++ b/internal/connector/test/integration/feature_test.go @@ -3,6 +3,7 @@ package integration import ( "os" "testing" + "time" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/config" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/client/keycloak" @@ -23,7 +24,7 @@ func TestMain(m *testing.M) { h, teardown := test.NewHelperWithHooks(t, ocmServer, func(c *config.ConnectorsConfig, kc *keycloak.KeycloakConfig) { c.ConnectorCatalogDirs = []string{"./internal/connector/test/integration/connector-catalog"} - c.ConnectorEvalDurationString = "30m" + c.ConnectorEvalDuration, _ = time.ParseDuration("30m") c.ConnectorEvalOrganizations = []string{"13640210"} kc.KeycloakClientExpire = true From 27d2731b65ff7fa7660d4cc401d079dbd9659bb1 Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Wed, 2 Mar 2022 15:33:33 -0800 Subject: [PATCH 07/12] fix(connectors): removed DeploymentLocation, replaced Tenant discriminator with enum, removed TargetKind in Connectors table --- .../api/admin/private/api/openapi.yaml | 32 +++----- .../model_connector_namespace_tenant.go | 6 +- .../model_connector_namespace_tenant_kind.go | 19 +++++ ...connector_namespace_tenant_organisation.go | 16 ---- .../model_connector_namespace_tenant_user.go | 16 ---- .../connector/internal/api/dbapi/connector.go | 13 ---- .../internal/api/public/api/openapi.yaml | 48 ++++-------- .../internal/api/public/model_connector.go | 34 ++++----- .../api/public/model_connector_meta.go | 18 ++--- .../model_connector_namespace_request.go | 3 +- .../model_connector_namespace_tenant.go | 6 +- .../model_connector_namespace_tenant_kind.go | 19 +++++ ...connector_namespace_tenant_organisation.go | 16 ---- .../model_connector_namespace_tenant_user.go | 16 ---- .../api/public/model_connector_request.go | 18 ++--- .../public/model_connector_request_meta.go | 10 +-- .../api/public/model_deployment_location.go | 15 ---- .../connector/internal/generated/bindata.go | 4 +- .../internal/handlers/connector_namespace.go | 16 ++-- .../connector/internal/handlers/connectors.go | 6 +- ...2203020000_delete_connector_target_kind.go | 19 +++++ .../internal/migrations/migrations.go | 1 + .../internal/presenters/connector.go | 10 +-- .../presenters/connector_namespace.go | 44 ++++++----- .../internal/presenters/connector_request.go | 3 +- .../internal/services/connector_namespaces.go | 4 +- .../internal/workers/connector_mgr.go | 73 +++++++++---------- .../features/connector-agent-api.feature | 16 ++-- .../features/connector-api.feature | 20 +---- .../connector-multitenancy-api.feature | 28 +++---- .../features/connector-old-path.feature | 2 - openapi/connector_mgmt.yaml | 48 ++++-------- pkg/handlers/validation_builder.go | 2 +- 33 files changed, 242 insertions(+), 359 deletions(-) create mode 100644 internal/connector/internal/api/admin/private/model_connector_namespace_tenant_kind.go delete mode 100644 internal/connector/internal/api/admin/private/model_connector_namespace_tenant_organisation.go delete mode 100644 internal/connector/internal/api/admin/private/model_connector_namespace_tenant_user.go create mode 100644 internal/connector/internal/api/public/model_connector_namespace_tenant_kind.go delete mode 100644 internal/connector/internal/api/public/model_connector_namespace_tenant_organisation.go delete mode 100644 internal/connector/internal/api/public/model_connector_namespace_tenant_user.go delete mode 100644 internal/connector/internal/api/public/model_deployment_location.go create mode 100644 internal/connector/internal/migrations/202203020000_delete_connector_target_kind.go diff --git a/internal/connector/internal/api/admin/private/api/openapi.yaml b/internal/connector/internal/api/admin/private/api/openapi.yaml index 3ed6a1ac1..c481980eb 100644 --- a/internal/connector/internal/api/admin/private/api/openapi.yaml +++ b/internal/connector/internal/api/admin/private/api/openapi.yaml @@ -953,31 +953,21 @@ components: - name type: object ConnectorNamespaceTenant: - discriminator: - mapping: - user: '#/components/schemas/ConnectorNamespaceTenantUser' - organisation: '#/components/schemas/ConnectorNamespaceTenantOrganisation' - propertyName: kind - oneOf: - - $ref: '#/components/schemas/ConnectorNamespaceTenantUser' - - $ref: '#/components/schemas/ConnectorNamespaceTenantOrganisation' - required: - - kind - type: object - ConnectorNamespaceTenantUser: - properties: - kind: - type: string - user_id: - type: string - type: object - ConnectorNamespaceTenantOrganisation: properties: kind: + $ref: '#/components/schemas/ConnectorNamespaceTenantKind' + id: + description: Either user or organisation id depending on the value of kind type: string - organisation_id: - type: string + required: + - id + - kind type: object + ConnectorNamespaceTenantKind: + enum: + - user + - organisation + type: string ConnectorNamespaceEvalRequest: allOf: - $ref: '#/components/schemas/ConnectorNamespaceRequestMeta' diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_tenant.go b/internal/connector/internal/api/admin/private/model_connector_namespace_tenant.go index d8e4e78d2..efe434aca 100644 --- a/internal/connector/internal/api/admin/private/model_connector_namespace_tenant.go +++ b/internal/connector/internal/api/admin/private/model_connector_namespace_tenant.go @@ -11,7 +11,7 @@ package private // ConnectorNamespaceTenant struct for ConnectorNamespaceTenant type ConnectorNamespaceTenant struct { - Kind string `json:"kind"` - UserId string `json:"user_id,omitempty"` - OrganisationId string `json:"organisation_id,omitempty"` + Kind ConnectorNamespaceTenantKind `json:"kind"` + // Either user or organisation id depending on the value of kind + Id string `json:"id"` } diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_tenant_kind.go b/internal/connector/internal/api/admin/private/model_connector_namespace_tenant_kind.go new file mode 100644 index 000000000..8e59e7c09 --- /dev/null +++ b/internal/connector/internal/api/admin/private/model_connector_namespace_tenant_kind.go @@ -0,0 +1,19 @@ +/* + * Connector Service Fleet Manager Admin APIs + * + * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. + * + * API version: 0.0.3 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package private + +// ConnectorNamespaceTenantKind the model 'ConnectorNamespaceTenantKind' +type ConnectorNamespaceTenantKind string + +// List of ConnectorNamespaceTenantKind +const ( + USER ConnectorNamespaceTenantKind = "user" + ORGANISATION ConnectorNamespaceTenantKind = "organisation" +) diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_tenant_organisation.go b/internal/connector/internal/api/admin/private/model_connector_namespace_tenant_organisation.go deleted file mode 100644 index a1b8a6948..000000000 --- a/internal/connector/internal/api/admin/private/model_connector_namespace_tenant_organisation.go +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Connector Service Fleet Manager Admin APIs - * - * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. - * - * API version: 0.0.3 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package private - -// ConnectorNamespaceTenantOrganisation struct for ConnectorNamespaceTenantOrganisation -type ConnectorNamespaceTenantOrganisation struct { - Kind string `json:"kind,omitempty"` - OrganisationId string `json:"organisation_id,omitempty"` -} diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_tenant_user.go b/internal/connector/internal/api/admin/private/model_connector_namespace_tenant_user.go deleted file mode 100644 index fd66f44cf..000000000 --- a/internal/connector/internal/api/admin/private/model_connector_namespace_tenant_user.go +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Connector Service Fleet Manager Admin APIs - * - * Connector Service Fleet Manager Admin is a Rest API to manage connector clusters. - * - * API version: 0.0.3 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package private - -// ConnectorNamespaceTenantUser struct for ConnectorNamespaceTenantUser -type ConnectorNamespaceTenantUser struct { - Kind string `json:"kind,omitempty"` - UserId string `json:"user_id,omitempty"` -} diff --git a/internal/connector/internal/api/dbapi/connector.go b/internal/connector/internal/api/dbapi/connector.go index c2d925313..aefd5d887 100644 --- a/internal/connector/internal/api/dbapi/connector.go +++ b/internal/connector/internal/api/dbapi/connector.go @@ -32,22 +32,9 @@ var AgentSetConnectorStatusPhase = []ConnectorStatusPhase{ ConnectorStatusPhaseDeleted, } -type TargetKind = string - -const ( - AddonTargetKind TargetKind = "addon" - CloudProviderTargetKind TargetKind = "cloud_provider" -) - -var AllTargetKind = []TargetKind{ - AddonTargetKind, - CloudProviderTargetKind, -} - type Connector struct { api.Meta - TargetKind TargetKind NamespaceId *string CloudProvider string Region string diff --git a/internal/connector/internal/api/public/api/openapi.yaml b/internal/connector/internal/api/public/api/openapi.yaml index b4775727e..ab39a8b66 100644 --- a/internal/connector/internal/api/public/api/openapi.yaml +++ b/internal/connector/internal/api/public/api/openapi.yaml @@ -1483,8 +1483,7 @@ components: ConnectorCreateExample: value: name: MyLogger - deployment_location: - namespace_id: 9bsv0s7tne7g02gh5g4g + namespace_id: 9bsv0s7tne7g02gh5g4g kafka: id: 9bsv0s6brfr002pfnkh0 client_id: srvc-acct-162ef2d8-0209-4117-8462-df63c2025c26 @@ -1758,11 +1757,6 @@ components: required: - client_id - client_secret - DeploymentLocation: - properties: - namespace_id: - type: string - type: object VersionMetadata: allOf: - $ref: '#/components/schemas/ObjectReference' @@ -1865,15 +1859,14 @@ components: type: string connector_type_id: type: string + namespace_id: + type: string channel: $ref: '#/components/schemas/Channel' - deployment_location: - $ref: '#/components/schemas/DeploymentLocation' desired_state: $ref: '#/components/schemas/ConnectorDesiredState' required: - connector_type_id - - deployment_location - desired_state - name ConnectorRequest: @@ -1932,31 +1925,21 @@ components: allOf: - $ref: '#/components/schemas/ObjectMeta' - $ref: '#/components/schemas/ConnectorNamespaceRequestMeta' + ConnectorNamespaceTenantKind: + enum: + - user + - organisation + type: string ConnectorNamespaceTenant: - discriminator: - mapping: - user: '#/components/schemas/ConnectorNamespaceTenantUser' - organisation: '#/components/schemas/ConnectorNamespaceTenantOrganisation' - propertyName: kind - oneOf: - - $ref: '#/components/schemas/ConnectorNamespaceTenantUser' - - $ref: '#/components/schemas/ConnectorNamespaceTenantOrganisation' - required: - - kind - type: object - ConnectorNamespaceTenantUser: properties: kind: + $ref: '#/components/schemas/ConnectorNamespaceTenantKind' + id: + description: Either user or organisation id depending on the value of kind type: string - user_id: - type: string - type: object - ConnectorNamespaceTenantOrganisation: - properties: - kind: - type: string - organisation_id: - type: string + required: + - id + - kind type: object ConnectorNamespaceRequest: allOf: @@ -2083,8 +2066,7 @@ components: cluster_id: type: string kind: - description: One of 'user' or 'organisation' similar to 'ConnectorNamespaceTenant' - type: string + $ref: '#/components/schemas/ConnectorNamespaceTenantKind' ConnectorNamespaceList_allOf: properties: items: diff --git a/internal/connector/internal/api/public/model_connector.go b/internal/connector/internal/api/public/model_connector.go index a8f57d761..a8d034611 100644 --- a/internal/connector/internal/api/public/model_connector.go +++ b/internal/connector/internal/api/public/model_connector.go @@ -15,21 +15,21 @@ import ( // Connector struct for Connector type Connector struct { - Id string `json:"id,omitempty"` - Kind string `json:"kind,omitempty"` - Href string `json:"href,omitempty"` - Owner string `json:"owner,omitempty"` - CreatedAt time.Time `json:"created_at,omitempty"` - ModifiedAt time.Time `json:"modified_at,omitempty"` - Name string `json:"name"` - ConnectorTypeId string `json:"connector_type_id"` - Channel Channel `json:"channel,omitempty"` - DeploymentLocation DeploymentLocation `json:"deployment_location"` - DesiredState ConnectorDesiredState `json:"desired_state"` - ResourceVersion int64 `json:"resource_version,omitempty"` - Kafka KafkaConnectionSettings `json:"kafka"` - ServiceAccount ServiceAccount `json:"service_account"` - SchemaRegistry SchemaRegistryConnectionSettings `json:"schema_registry,omitempty"` - Connector map[string]interface{} `json:"connector"` - Status ConnectorStatusStatus `json:"status,omitempty"` + Id string `json:"id,omitempty"` + Kind string `json:"kind,omitempty"` + Href string `json:"href,omitempty"` + Owner string `json:"owner,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty"` + ModifiedAt time.Time `json:"modified_at,omitempty"` + Name string `json:"name"` + ConnectorTypeId string `json:"connector_type_id"` + NamespaceId string `json:"namespace_id,omitempty"` + Channel Channel `json:"channel,omitempty"` + DesiredState ConnectorDesiredState `json:"desired_state"` + ResourceVersion int64 `json:"resource_version,omitempty"` + Kafka KafkaConnectionSettings `json:"kafka"` + ServiceAccount ServiceAccount `json:"service_account"` + SchemaRegistry SchemaRegistryConnectionSettings `json:"schema_registry,omitempty"` + Connector map[string]interface{} `json:"connector"` + Status ConnectorStatusStatus `json:"status,omitempty"` } diff --git a/internal/connector/internal/api/public/model_connector_meta.go b/internal/connector/internal/api/public/model_connector_meta.go index b6dbc0560..f4e78e591 100644 --- a/internal/connector/internal/api/public/model_connector_meta.go +++ b/internal/connector/internal/api/public/model_connector_meta.go @@ -15,13 +15,13 @@ import ( // ConnectorMeta struct for ConnectorMeta type ConnectorMeta struct { - Owner string `json:"owner,omitempty"` - CreatedAt time.Time `json:"created_at,omitempty"` - ModifiedAt time.Time `json:"modified_at,omitempty"` - Name string `json:"name"` - ConnectorTypeId string `json:"connector_type_id"` - Channel Channel `json:"channel,omitempty"` - DeploymentLocation DeploymentLocation `json:"deployment_location"` - DesiredState ConnectorDesiredState `json:"desired_state"` - ResourceVersion int64 `json:"resource_version,omitempty"` + Owner string `json:"owner,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty"` + ModifiedAt time.Time `json:"modified_at,omitempty"` + Name string `json:"name"` + ConnectorTypeId string `json:"connector_type_id"` + NamespaceId string `json:"namespace_id,omitempty"` + Channel Channel `json:"channel,omitempty"` + DesiredState ConnectorDesiredState `json:"desired_state"` + ResourceVersion int64 `json:"resource_version,omitempty"` } diff --git a/internal/connector/internal/api/public/model_connector_namespace_request.go b/internal/connector/internal/api/public/model_connector_namespace_request.go index da11f8e47..fd521f7c8 100644 --- a/internal/connector/internal/api/public/model_connector_namespace_request.go +++ b/internal/connector/internal/api/public/model_connector_namespace_request.go @@ -14,6 +14,5 @@ type ConnectorNamespaceRequest struct { Name string `json:"name"` Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` ClusterId string `json:"cluster_id"` - // One of 'user' or 'organisation' similar to 'ConnectorNamespaceTenant' - Kind string `json:"kind"` + Kind ConnectorNamespaceTenantKind `json:"kind"` } diff --git a/internal/connector/internal/api/public/model_connector_namespace_tenant.go b/internal/connector/internal/api/public/model_connector_namespace_tenant.go index 982d44848..e376c5669 100644 --- a/internal/connector/internal/api/public/model_connector_namespace_tenant.go +++ b/internal/connector/internal/api/public/model_connector_namespace_tenant.go @@ -11,7 +11,7 @@ package public // ConnectorNamespaceTenant struct for ConnectorNamespaceTenant type ConnectorNamespaceTenant struct { - Kind string `json:"kind"` - UserId string `json:"user_id,omitempty"` - OrganisationId string `json:"organisation_id,omitempty"` + Kind ConnectorNamespaceTenantKind `json:"kind"` + // Either user or organisation id depending on the value of kind + Id string `json:"id"` } diff --git a/internal/connector/internal/api/public/model_connector_namespace_tenant_kind.go b/internal/connector/internal/api/public/model_connector_namespace_tenant_kind.go new file mode 100644 index 000000000..4d193cdbf --- /dev/null +++ b/internal/connector/internal/api/public/model_connector_namespace_tenant_kind.go @@ -0,0 +1,19 @@ +/* + * Connector Service Fleet Manager + * + * Connector Service Fleet Manager is a Rest API to manage connectors. + * + * API version: 0.1.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package public + +// ConnectorNamespaceTenantKind the model 'ConnectorNamespaceTenantKind' +type ConnectorNamespaceTenantKind string + +// List of ConnectorNamespaceTenantKind +const ( + CONNECTORNAMESPACETENANTKIND_USER ConnectorNamespaceTenantKind = "user" + CONNECTORNAMESPACETENANTKIND_ORGANISATION ConnectorNamespaceTenantKind = "organisation" +) diff --git a/internal/connector/internal/api/public/model_connector_namespace_tenant_organisation.go b/internal/connector/internal/api/public/model_connector_namespace_tenant_organisation.go deleted file mode 100644 index 10dbdc7ea..000000000 --- a/internal/connector/internal/api/public/model_connector_namespace_tenant_organisation.go +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Connector Service Fleet Manager - * - * Connector Service Fleet Manager is a Rest API to manage connectors. - * - * API version: 0.1.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package public - -// ConnectorNamespaceTenantOrganisation struct for ConnectorNamespaceTenantOrganisation -type ConnectorNamespaceTenantOrganisation struct { - Kind string `json:"kind,omitempty"` - OrganisationId string `json:"organisation_id,omitempty"` -} diff --git a/internal/connector/internal/api/public/model_connector_namespace_tenant_user.go b/internal/connector/internal/api/public/model_connector_namespace_tenant_user.go deleted file mode 100644 index 3b936c491..000000000 --- a/internal/connector/internal/api/public/model_connector_namespace_tenant_user.go +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Connector Service Fleet Manager - * - * Connector Service Fleet Manager is a Rest API to manage connectors. - * - * API version: 0.1.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package public - -// ConnectorNamespaceTenantUser struct for ConnectorNamespaceTenantUser -type ConnectorNamespaceTenantUser struct { - Kind string `json:"kind,omitempty"` - UserId string `json:"user_id,omitempty"` -} diff --git a/internal/connector/internal/api/public/model_connector_request.go b/internal/connector/internal/api/public/model_connector_request.go index 1449adb30..917ebaaff 100644 --- a/internal/connector/internal/api/public/model_connector_request.go +++ b/internal/connector/internal/api/public/model_connector_request.go @@ -11,13 +11,13 @@ package public // ConnectorRequest struct for ConnectorRequest type ConnectorRequest struct { - Name string `json:"name"` - ConnectorTypeId string `json:"connector_type_id"` - Channel Channel `json:"channel,omitempty"` - DeploymentLocation DeploymentLocation `json:"deployment_location"` - DesiredState ConnectorDesiredState `json:"desired_state"` - Kafka KafkaConnectionSettings `json:"kafka"` - ServiceAccount ServiceAccount `json:"service_account"` - SchemaRegistry SchemaRegistryConnectionSettings `json:"schema_registry,omitempty"` - Connector map[string]interface{} `json:"connector"` + Name string `json:"name"` + ConnectorTypeId string `json:"connector_type_id"` + NamespaceId string `json:"namespace_id,omitempty"` + Channel Channel `json:"channel,omitempty"` + DesiredState ConnectorDesiredState `json:"desired_state"` + Kafka KafkaConnectionSettings `json:"kafka"` + ServiceAccount ServiceAccount `json:"service_account"` + SchemaRegistry SchemaRegistryConnectionSettings `json:"schema_registry,omitempty"` + Connector map[string]interface{} `json:"connector"` } diff --git a/internal/connector/internal/api/public/model_connector_request_meta.go b/internal/connector/internal/api/public/model_connector_request_meta.go index ad9a71fc3..a622f6466 100644 --- a/internal/connector/internal/api/public/model_connector_request_meta.go +++ b/internal/connector/internal/api/public/model_connector_request_meta.go @@ -11,9 +11,9 @@ package public // ConnectorRequestMeta struct for ConnectorRequestMeta type ConnectorRequestMeta struct { - Name string `json:"name"` - ConnectorTypeId string `json:"connector_type_id"` - Channel Channel `json:"channel,omitempty"` - DeploymentLocation DeploymentLocation `json:"deployment_location"` - DesiredState ConnectorDesiredState `json:"desired_state"` + Name string `json:"name"` + ConnectorTypeId string `json:"connector_type_id"` + NamespaceId string `json:"namespace_id,omitempty"` + Channel Channel `json:"channel,omitempty"` + DesiredState ConnectorDesiredState `json:"desired_state"` } diff --git a/internal/connector/internal/api/public/model_deployment_location.go b/internal/connector/internal/api/public/model_deployment_location.go deleted file mode 100644 index b25164f3c..000000000 --- a/internal/connector/internal/api/public/model_deployment_location.go +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Connector Service Fleet Manager - * - * Connector Service Fleet Manager is a Rest API to manage connectors. - * - * API version: 0.1.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package public - -// DeploymentLocation struct for DeploymentLocation -type DeploymentLocation struct { - NamespaceId string `json:"namespace_id,omitempty"` -} diff --git a/internal/connector/internal/generated/bindata.go b/internal/connector/internal/generated/bindata.go index ba85b0e3d..4b9e8231a 100644 --- a/internal/connector/internal/generated/bindata.go +++ b/internal/connector/internal/generated/bindata.go @@ -78,7 +78,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _connector_mgmtYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x7b\x73\xdb\xb8\xb5\xf8\xff\xfe\x14\xf8\x29\xbf\x8e\xdb\x7b\x23\x59\x92\xe5\x97\xe6\xa6\x33\x4e\xec\x64\xbd\xeb\x38\x59\xdb\xd9\x6c\xda\xe9\xc8\x10\x09\x49\x88\x49\x80\x06\x40\x25\x4a\x7b\xbf\xfb\x1d\x00\x7c\x80\x24\x48\x51\xb2\x62\x3b\xbb\xe4\x4c\xbb\x31\x09\x1c\x1c\x1c\x1c\x9c\x37\x20\x1a\x20\x02\x03\x3c\x04\xbb\x9d\x6e\xa7\x0b\x9e\x01\x82\x90\x0b\xc4\x0c\x73\x00\x39\x98\x60\xc6\x05\xf0\x30\x41\x40\x50\x00\x3d\x8f\x7e\x01\x9c\xfa\x08\x9c\x9d\x9c\x72\xf9\xea\x96\xd0\x2f\xba\xb5\xec\x40\x40\x04\x0e\xb8\xd4\x09\x7d\x44\x44\x67\xeb\x19\x38\xf6\x3c\x80\x88\x1b\x50\x4c\x04\x07\x2e\x9a\x60\x82\x5c\x30\x43\x0c\x81\x2f\xd8\xf3\xc0\x18\x01\x17\x73\x87\xce\x11\x83\x63\x0f\x81\xf1\x42\x8e\x04\x42\x8e\x18\xef\x80\xb3\x09\x10\xaa\xad\x1c\x20\xc2\x8e\x82\x5b\x84\x02\x8d\x49\x02\x79\xeb\x19\x68\x05\x0c\xcf\xa1\x40\xad\xe7\x00\xba\x72\x16\xc8\x97\x8d\xc5\x0c\x81\x96\x43\x09\x41\x8e\xa0\x6c\xe4\x4f\x7d\xd1\x8e\x5a\x76\x16\xd0\xf7\x5a\x60\x82\x3d\xb4\x85\xc9\x84\x0e\xb7\x00\x10\x58\x78\x68\x08\x5e\xc5\x1d\xc0\x15\x62\x73\xec\x20\xf0\xda\x43\x48\x80\xb7\x90\xc0\x29\x62\x5b\x00\xcc\x11\xe3\x98\x92\x21\xe8\x76\x7a\x9d\xee\x16\x00\x2e\xe2\x0e\xc3\x81\x50\x2f\x97\xf4\xd7\xf3\xb9\x44\x5c\x80\xe3\xf7\x67\x12\x4d\x5f\x7d\x00\x09\xa2\xbc\xb3\xc5\x11\x93\x83\x48\xac\xda\x20\x64\xde\x10\xcc\x84\x08\xf8\x70\x67\x07\x06\xb8\x23\x89\xcd\x67\x78\x22\x3a\x0e\xf5\xb7\x00\xc8\x21\xf0\x16\x62\x02\xfe\x1a\x30\xea\x86\x8e\x7c\xf3\x37\xa0\xc1\xd9\x81\x71\x01\xa7\x68\x19\xc8\x2b\x01\xa7\x98\x4c\xad\x80\x86\x3b\x3b\x1e\x75\xa0\x37\xa3\x5c\x0c\x0f\xbb\xdd\x6e\xb1\x7b\xf2\x3d\xed\xb9\x53\x6c\xe5\x84\x8c\x21\x22\x80\x4b\x7d\x88\xc9\x96\x80\xd3\x88\x00\x04\xfa\x99\x75\xb9\x5e\x04\x88\x17\xfb\xb7\x5a\xb6\xd6\xb5\x1b\x82\x57\x5e\xc8\x05\x5a\xa1\x43\xb4\xbe\xd6\xf6\x5b\x01\x14\x33\x85\xff\x33\xf9\x3f\x60\xed\xf6\x6c\x6b\x0b\x80\x96\x5c\x86\x9d\x2c\x9b\xee\xcc\x7b\xad\xa1\x82\x3b\x45\x42\xff\x03\x80\x98\x20\xfa\x69\x97\x20\x02\xe4\x5e\x64\x50\x22\x72\xe6\x0e\x65\xff\xdf\x34\xbb\xbe\x45\x02\xba\x50\xc0\xa8\x15\x0f\x7d\x1f\xb2\xc5\x10\x5c\x22\x11\x32\xc2\xd5\x6e\x89\x38\x1b\xf8\xd9\xb6\x99\xc9\xd5\x68\xcf\x10\x0f\x28\xe1\xc8\x40\xb7\xd5\xef\x76\x5b\xe9\x9f\x40\xb2\xbb\x40\x44\x98\xaf\x00\x80\x41\xe0\x61\x47\x21\xbf\xf3\x99\x53\x92\xfd\x0a\x00\x77\x66\xc8\x87\xf9\xb7\x00\xfc\x7f\x86\x26\x43\xb0\xfd\x6c\xc7\xa1\x7e\x40\x09\x22\x82\xef\xe8\xb6\x7c\x27\x37\xfd\x6d\xa3\x73\x66\x5e\xbf\xe5\xe7\x92\xac\x5d\x91\xf3\xaa\x16\x6e\xe7\x16\x4e\x6e\xe1\x28\x7d\x2f\x64\xa7\x9d\x7f\x67\x5f\x8c\xb0\xfb\xbf\x11\x3d\x02\xc8\xa0\x8f\x44\xb4\xdf\xf5\xda\x6a\x56\x2b\x74\xd9\xb2\x62\x7e\x3d\x43\x00\xbb\x80\x2a\x89\x99\x76\x02\xb2\xd3\x56\x39\xe9\xe4\xe7\x21\xe0\x82\x61\x32\x4d\x5e\x63\x32\x04\x92\x75\x93\x17\x0c\xdd\x85\x98\x21\x77\x08\x04\x0b\x51\x7d\x9e\x4c\x37\x29\x00\x1c\x39\x21\xc3\x62\x61\xb6\x7c\x89\x20\x43\x6c\x08\xfe\x09\xfe\x55\xc2\xb7\x09\x2c\x09\xea\xe5\xe2\xec\x24\xcf\xb9\x6f\x90\x00\x30\x37\x5f\xa9\x45\x12\x3a\x65\xa8\xb4\xb4\xf5\x23\x71\x6d\xcb\xca\xb5\x99\xc9\xb7\x72\x5d\xd1\x57\xe8\x07\x9e\x89\x68\xfc\x64\xba\x9d\xea\x66\xc5\x56\xf6\xa1\x63\xa8\x3b\x36\x20\xad\xb2\x6d\x73\x5d\x60\x39\xe0\x43\xe1\xcc\xa4\xba\x90\xec\x28\xf9\x07\x29\xc9\x1f\x91\x74\xd0\xed\x3d\x0e\x49\x4f\x19\xa3\xac\x3e\x29\x07\xdd\xde\xba\x04\x4c\xbb\x96\x92\xed\x38\x14\x33\x20\xe8\x2d\x22\xd2\x20\xc0\x64\x0e\x3d\x63\x7b\xb7\x06\xdd\xc1\x0f\x42\xa4\xc1\xfa\x44\x1a\x2c\x23\xd2\x05\x4d\x79\x29\xc7\x63\xe8\x2b\xe6\x82\xa7\x04\xdb\x7b\xac\x8d\xba\x22\xc1\xf6\xba\xdd\x75\x09\x96\x76\x2d\x25\xd8\x07\x82\xbe\x06\xc8\x11\xc8\x05\x48\xe2\x05\xa8\xa3\xac\x2a\x77\x65\x7d\xb5\x8a\xf9\xb1\x61\x51\xcf\xcb\x2c\x14\x08\x3c\xcc\x85\xd4\x73\x59\x66\xe0\x55\x66\xca\xb2\x4e\x45\xed\x2b\x51\xb6\x2d\x44\xda\x72\x27\x80\x53\x63\x11\x96\x36\xe7\xf8\xdb\x2a\xcd\x29\x73\x11\x7b\xb9\x58\x65\x00\x04\x99\x33\x6b\x3d\x79\x45\x76\x8e\xb9\x28\x17\x89\x4b\x56\xaa\xd1\x1d\xf5\x74\x47\x23\x0a\x97\x8a\xc2\x9c\x5d\xbf\xa2\x45\x1f\x0b\xc7\x40\x7a\xbc\xcb\xa4\xe3\x3d\x04\xa3\xc3\x10\x14\xc8\xc4\x12\x98\x62\xf1\x95\xfa\xac\x82\x23\x5f\xd2\x2d\x63\x93\x85\x95\x2d\xed\x02\x50\xfa\x01\x77\x21\x62\x0b\x83\xbe\xda\x29\x81\x7c\x41\x9c\x32\xaa\xbf\x47\x6c\x42\x99\xaf\x2c\x3f\xa8\xa2\x0f\x00\x13\x00\x89\xee\x35\x63\x94\xd0\x90\x03\x1f\x12\x82\xd8\x56\x35\xb7\x69\xf7\x64\x4c\xa9\x87\x20\x31\xbe\x58\x1c\x12\x10\x5b\x99\x2f\xa9\x6b\x10\xb8\x24\x2c\x63\x38\xaa\xd6\xcd\x51\xbd\x35\xec\x1b\xa3\x96\x04\xbc\xd4\x48\x66\x77\x48\xd9\xfe\x48\x7a\xe9\xc5\x2b\xdd\x29\xf5\x2c\xf9\x0c\x90\x56\x95\x73\x57\xa6\x3e\xfa\x8f\xac\x3e\xca\xa5\xa1\xe3\xa0\x40\xa0\x8c\xf1\xfc\x63\x08\xc0\x41\xb7\xab\xd6\x05\x53\xb2\xbe\xb6\xc8\x83\x28\xa5\xd3\x6f\x52\x4b\xa8\x96\x5a\x20\xf2\x54\x22\x1a\x94\x6b\xf4\x6b\xe3\x9b\xd5\xf2\xcd\xae\x53\xdf\x1e\xb9\x52\x66\xd0\x90\x39\x08\xb8\x14\x71\xb2\x2d\xb4\x7f\xd6\xd8\x24\x39\xc6\x22\x20\x2c\x33\x4b\xb4\xb6\x8f\xa3\x26\x59\x25\x5d\xc7\x0b\xbb\x87\x9d\x21\xcd\xee\x22\x9c\x3f\x98\xf7\xf5\xa4\x7d\xa3\x55\xfd\xa2\xc6\x25\x6a\x5c\xa2\xc7\x89\x0e\xf1\x9d\x7f\x57\x67\x2e\x96\x6c\x46\xec\xb6\x1e\x42\xa4\x99\x31\xa5\xbc\x40\xcb\x25\x02\x6c\xe2\xcb\xde\xe4\x69\xca\x8e\x9a\x91\xf9\x26\x28\xdf\x18\x7e\x75\x88\xd4\x04\xe5\x57\x22\xd8\xbd\xc4\xae\x6e\xea\x21\x81\xbe\xa7\x2c\xd4\x23\x94\x8a\xc3\x13\xf5\x79\x99\x44\x2c\x6d\x65\x17\x8a\x4f\x65\xa3\x58\xe6\xd0\xb8\xbb\x7f\x58\xa9\xa7\x17\xf8\x1e\xb2\x2f\x03\xa0\x4a\x02\x2a\xab\x28\x56\xa3\xe0\x0b\x16\x33\xc0\x03\xe4\xe0\x09\x46\x2e\x38\x3b\xf9\x91\x25\xe1\xfd\x88\x98\x07\xb0\xa6\x54\x0c\xa4\x86\xf9\x9e\x42\x51\x0d\x50\x2a\x13\xdf\xcb\xaf\xcb\x44\x62\x59\xa3\xe5\xb1\xe8\x13\x28\x20\x10\x54\x23\x91\x2b\xda\x91\xbc\xb4\x55\xc1\x26\x26\x93\xf8\x88\x4d\x51\x5b\x41\xf9\xef\xba\x91\x6a\x1d\x56\xa7\xe3\xcf\xc8\x11\x25\x60\x25\xa8\x15\xa1\xe6\x1c\xd6\x9f\xaf\xde\x5d\x68\xfa\x3c\x07\x97\xaf\x5f\x81\xfd\xa3\x6e\x1f\xb4\x93\xba\x43\x41\xa9\xc7\x3b\x18\x89\x49\x87\xb2\xe9\xce\x4c\xf8\xde\x0e\x9b\x38\xb2\xd5\x7a\xd8\x6e\x3e\x44\x9f\x74\xfe\x23\x84\xc8\x1b\x4f\xe0\xcf\xab\x13\x1b\x4f\xe0\x47\xf0\x04\x2c\xb5\xa6\x51\x39\xf2\xaa\xd5\xa6\x4e\x54\xc5\xbc\x4a\x8e\x3a\x5b\xfa\x5c\x9d\x85\x4e\xd1\x02\xb5\x35\xef\x92\x94\x35\x70\x32\x30\x6b\xa4\xae\x73\x3d\xfe\x74\x29\xec\x68\xfa\x8f\x97\xca\x8e\xb8\x60\xcd\x8c\xb6\xee\xbc\x99\xc4\xb6\x05\xd6\x0f\x99\xdf\x8e\x26\xd2\xa4\xb9\x9b\x34\xb7\x41\xb9\xc6\xc6\xa9\x41\xa4\x26\xcd\xfd\xb4\xcc\x9c\x35\xd2\xdc\x19\x85\x5e\xab\xe8\x38\x67\xb2\xdc\x37\xed\x9d\x07\x57\x27\xfb\xed\x64\xfb\xd4\x4e\x80\xe7\xfa\x3d\x74\x05\xf2\xd3\x4c\x63\x45\x0b\xb0\x72\x85\x70\x8e\x98\x8d\x74\x6f\x32\xe2\x0f\x7c\x5e\x22\xe6\x40\xf3\x88\x5f\xf4\x6e\xc5\x53\x7e\x69\x2f\xbb\x03\x50\x76\xd0\x2f\xeb\x0d\x3d\xfc\x59\xbf\xfb\xcb\x62\x33\x5f\x9f\xf3\x30\xcb\x4e\xfb\x55\x38\x8d\xd5\x4d\x9f\xb4\xfc\xab\x19\xc3\x8b\x1d\xc0\x26\x96\xd7\xd8\xb9\x75\x88\xb4\x66\x2c\x2f\x66\xb3\x26\xa6\xb7\x6e\x1e\x2b\x7c\x10\xf1\x19\x06\xae\x25\x46\xf7\x72\x71\xe6\xe6\xa5\x68\xe8\x06\x30\x9b\xc7\xaf\x12\xa4\x4b\x5b\xd7\xcf\x75\x69\x14\xdd\x35\x33\x5d\x0f\x12\xbc\x5a\x21\x5a\x94\x15\x19\xd9\x28\x5d\xb4\x67\xb8\x80\x22\x54\xf7\xa3\x44\x53\x6f\xe4\x72\x23\x97\x37\x2c\x97\x1b\x91\x6c\xa3\xd9\x46\x0a\xae\x36\x20\x95\x73\x85\x57\x25\x76\x6d\xb1\xb2\xaa\x4a\x22\x2f\x6d\xdd\xd4\x63\x35\x72\xf1\x89\x10\xe9\x01\xeb\xb1\x92\xc0\x6c\x53\x8a\xb5\xc9\x52\xac\xcd\x45\x41\x76\xa0\xeb\x52\x32\x4a\xa3\x20\x4d\x58\x64\xbd\xb0\xc8\xb1\xa4\xe3\xfb\x84\x6a\x79\x6d\x52\x12\xfa\xd8\xe6\x40\x2d\x80\x41\x6f\x9b\x76\x59\xb9\xf7\x93\x8a\xa5\x64\x49\x53\x19\x49\x96\x2c\x93\x4e\x06\x88\x19\x14\x80\xcf\x68\xe8\xb9\x60\x8c\x40\xc8\xf5\x6d\x83\x0e\x25\x13\x3c\x0d\x19\x52\x8c\xa5\xef\xe9\x33\x3d\x18\x4d\x14\x4a\x34\xdf\x69\x5a\x75\x1a\x75\xf6\x47\x55\x67\x4d\xf8\xe5\x47\xb2\xf5\x37\xa8\xbb\xa4\x42\xe2\x01\x74\xd0\x0f\xae\xb5\x56\xcc\x2b\xae\x94\x55\x5c\xf5\x56\xa3\x95\xee\x34\x7a\x3c\x75\x7b\x91\x2c\x7d\x7d\x4d\x4b\xf2\x7d\x6a\xea\xd8\x42\xbf\x27\xa5\x5d\x13\xca\x24\x24\x59\xaa\x61\xd3\x09\x81\x39\xe6\x78\xec\xa9\xeb\x84\x43\x8e\x18\xc0\x8d\xd2\x6c\x94\x66\xa3\x34\x9f\xa0\xd2\xcc\xd7\x21\x67\x24\xe0\x4a\xa5\xc8\x45\xb5\x59\xab\x18\xb9\x20\x72\xab\xca\x91\x93\xc6\x2b\x49\xfd\x65\x05\xc9\x24\x07\xb5\x4e\x49\x72\xbe\xcf\x2a\xf5\xbc\x49\xdf\xc7\xab\xe8\x4d\x08\xb9\x5e\x4d\x6f\xd2\x7d\x23\x55\xbd\x76\x68\x3f\x64\x5d\x6f\x32\x95\xa6\xb2\xb7\xa9\xec\x35\x28\xd7\x58\x0f\x35\x88\xd4\x54\xf6\x3e\x2d\xc3\x61\x9d\xca\xde\xac\x5e\xac\xe5\xc9\x15\x9d\xae\x7b\x56\xf7\x96\x7b\x71\x55\x75\xba\xd5\x7e\xdc\x4a\x3d\x9b\x5b\x86\xd3\xe7\x09\x38\xa7\xb6\x42\xe2\xc2\x9a\x35\xea\xa4\x29\x25\x7e\xe0\x40\x64\xca\x83\x66\x28\x32\x79\xbb\x62\x39\xb1\xd9\xcf\xee\x81\x94\xc5\x20\xf3\xbe\xcc\xc3\xe7\xce\x36\xa1\x02\xcc\x70\x5e\xc1\x4f\x2c\x0b\xe3\x55\xba\x7e\xcb\x1a\x3f\x71\x99\x58\xb3\xb8\x38\xf5\x46\x9b\xf2\xe2\xc6\xd8\xae\x43\xa4\x35\x43\x75\x29\xa3\x35\xc1\xba\xef\x59\x60\xbc\x09\x61\x9a\x2b\x31\x4e\x40\xd6\x2c\x32\xae\x14\xab\x35\xda\xff\x90\x85\xc6\xe5\x31\xb5\xcd\x94\x1a\x27\xf0\x9b\x62\xe3\x46\x4a\x3f\x80\x94\x6e\x04\xb4\x8d\x6a\x9b\x29\x37\xde\x84\x8c\xce\x15\x1c\x97\xda\xbc\x96\x22\xe2\x4a\xf9\x5c\xa3\x7d\x53\x76\xdc\x48\xc8\x27\x42\xa4\xa6\xec\xf8\x4f\x54\x76\x6c\x44\x4c\xd0\x1c\x7a\x1b\x4f\x34\x9f\xce\xa1\x17\xaa\x77\x9b\xcc\x34\xf3\x19\x65\x02\x78\x78\x2e\xe7\x9e\x8c\xb0\x56\x02\xba\x56\xf7\x1f\x35\x17\x2d\xa9\x7f\xcf\x7c\xb4\x04\xb1\xd9\x9c\x74\x01\x62\x93\x97\xfe\xde\x58\x37\x79\xe9\x07\xa3\x5c\x63\x62\xd4\x20\x52\x93\x97\x7e\x5a\x2e\xd8\xfd\xf2\xd2\x5b\xe9\xa8\x12\xb9\x68\x86\x43\x7d\x07\xf0\x33\xfd\xff\xe0\x15\xf5\x7d\x4a\xa2\x57\xea\x3f\xe7\x38\x35\x32\x12\xc1\x6f\x18\x03\xb7\x98\xb8\xc6\x9f\x01\x9c\x22\xe3\x4f\x8e\xbf\x99\x7f\x0a\x2a\xa0\x67\xfc\x8d\x05\xf2\x63\xb3\xc4\x72\x09\x72\xc0\xa4\xad\x22\xb0\x49\x6a\x39\xde\xd2\x04\x8d\xc4\xa2\xd8\x08\x13\x81\xa6\x66\xcd\x39\xfe\x56\xa3\x95\xc2\xb9\xbc\x99\xfa\xa0\xd8\x24\x6e\x03\x3d\xef\xdd\xc4\x24\x51\x15\x83\xbd\x53\xf3\xbd\x44\x13\xc4\x10\x71\x32\x99\xed\x92\x5b\xa1\x6d\x44\x01\x6a\x4f\xb8\x05\xae\xb3\x12\x07\xa8\x95\x84\x96\x1d\x52\xda\x3c\x31\x19\x47\xd8\xad\xec\xa4\xbe\xe5\xe6\x34\x5c\x6d\x81\xf1\xf2\xe5\xad\xc5\x03\x33\x49\xf5\xb2\x46\x06\x9e\x6f\x91\x80\x2b\xa2\x48\xbf\x10\xc4\x96\x22\xa0\x4d\x6b\x77\x04\x33\x72\x6a\x42\x99\x0f\xc5\x50\x9a\x9d\xa8\x2d\xb0\x8f\x96\x81\xf1\xa9\xab\xdc\xad\x75\xe1\xa8\xf7\x57\x88\xcd\xb1\x13\x07\x4d\x30\x25\x57\x48\x48\x69\xc1\xab\xb6\x36\x36\x37\x76\xc8\xbc\xfb\x2d\x5a\xc8\x2c\xbb\xc8\x82\xe3\xb1\xe3\xd0\x90\x54\xca\x1c\xc7\xc3\x88\x88\x51\x06\xbf\xe8\x1d\x47\x0e\x43\x55\x6b\x97\xf4\x5d\xbe\x7e\x26\xc4\x6a\xd4\x4f\x50\xe0\xd1\x85\x8f\x88\x38\xa7\x5a\x03\xad\xc8\x51\x66\x12\xbb\x7a\xa8\xdf\x10\xe3\x98\x12\xc9\xb5\xd2\x73\x79\x20\xa1\x83\x6c\x5a\x4d\x6d\x43\xd0\x3a\x7e\x7f\x16\x21\x95\x55\x94\x58\x7e\x9c\xf7\xb2\x2f\x67\x1a\x2d\xbb\xdf\xdb\xca\x09\x34\xcf\xd3\xcc\x5a\xd0\xb4\x6d\x0d\x5c\xb9\xc9\x3c\xaf\x9e\x97\x0c\x52\xfc\x9d\xe3\x42\xff\x68\x62\xa5\x3f\x5c\x57\x2e\x82\x4b\x31\xd6\x74\x85\x8c\xc1\x45\xee\x8b\xd2\x81\x45\x73\x21\xb7\xa0\xe6\xdc\x57\x5a\xda\x8c\x7a\x8f\xb6\x18\x37\x15\xfc\x2f\x92\x1c\xe5\x82\x21\x63\x81\xfc\x44\x3d\x97\xc7\x16\x86\x3a\x34\xaa\x3d\x02\x7d\x8a\x54\x42\x90\xff\x84\x1a\x26\x38\x23\x5c\x40\xe2\xa0\xce\x3a\x3c\x5a\x2a\xb1\xd2\x85\x78\x16\xfd\x40\x49\x14\x91\x72\x8c\x75\x49\xdb\x94\xb0\xf4\xb3\xec\x2a\x6a\x01\xa4\x86\xbe\x44\x53\xcc\x05\x5b\x6c\x98\x24\x0a\x38\x88\x81\x3f\x00\x6d\x74\x63\xc0\xe2\x11\x37\x45\xa5\x98\x97\xd4\x39\xe4\x0c\x27\x65\x4f\x26\x5b\xa9\xd5\x3a\xce\x9f\xb1\x6e\x6d\xdc\x3a\x98\x43\x2f\xb4\xd8\x75\xa6\x10\x2d\x9e\xa1\x2e\xc3\x36\xae\xa0\xcb\x9f\x0c\xcf\xa2\x6d\xee\xeb\xdc\x7e\xae\x7f\x94\xbb\x95\x37\xc5\x8b\x77\xe4\x27\xa4\xce\x1f\xe0\xbb\x12\x50\xe4\x0c\xad\x0c\x55\x10\x09\x7d\x93\xbb\x5c\xcc\x23\xee\x44\xa6\x12\x65\x08\xba\x0b\xfb\x08\x51\x80\xca\xb4\x96\xca\x14\x59\x35\xed\x4b\x00\xdb\x17\x40\x6f\x49\x69\xec\x98\xc5\x39\x69\x02\x1c\x40\x15\xbf\x03\x81\x07\x09\x32\x0e\x19\xea\x4c\x71\x6b\x9d\xcd\x55\x31\xf1\x96\x7d\x06\x26\x4d\xd6\xd0\xc3\x1a\xf2\xf7\x42\xee\x4a\x51\xa2\x6a\xc9\x78\xa6\x45\xc9\x5e\x2c\xeb\x1c\x03\x28\xb8\x1e\xab\xcc\x42\x71\x6f\x2e\xf4\x69\x7a\x54\xf5\x99\x69\xd3\xe6\xd0\x2a\xb3\xb8\xcf\x3a\x5e\x45\xfc\x6a\x9d\x94\x29\x9f\x56\x9a\x58\xd6\x6e\x59\xd9\xa3\xb4\x5a\x26\x2b\x1b\x32\xab\xdd\x0b\x6a\x17\x81\xc6\xdb\x57\x33\x48\x08\xf2\x2a\x64\x9d\x8b\x26\x30\xf4\x84\x7c\x0b\xc7\x1e\x2a\x91\x80\xd1\xc7\x2c\xc1\x4f\x10\x97\xbe\xc6\xaa\xd2\x54\x8b\x4d\x13\x36\x0d\x82\x8c\x60\x75\xa3\x6c\x6c\x76\xb8\x55\xc7\x81\x9c\xe3\x29\x31\x75\x5d\xfc\x2e\x33\x98\x12\x8d\xd9\x56\xcb\x31\x9c\x40\xec\x15\x51\xce\x42\x71\x73\x39\xe5\xb6\x64\x9d\x39\x96\xa6\x7f\xbe\x61\xe6\x43\x8e\xab\x4d\x3b\xa9\x32\xb4\x24\xad\x3b\x13\x69\x6d\xf6\x8c\xa0\xf6\x10\x8d\x2f\xf9\x1f\x27\xb3\x06\x8e\x24\x34\x93\x3d\xab\x18\xb3\xc4\x28\x4e\x37\x53\x0e\x97\x22\xdc\xed\x2a\xcb\x2d\xf2\x71\xb7\x53\x70\xea\xfb\x28\x36\xd6\xea\xa2\xb9\xcc\x62\x4d\xf1\x4d\x28\x64\x82\x7e\x66\x04\x0a\xab\xcc\x43\xd9\x52\xa9\x59\x3e\x83\x01\xca\xbc\x0e\x18\x75\x10\xe7\xe6\x8f\x75\xcb\xd7\x3a\x38\x39\x83\xc4\xf5\xb2\xb1\xa4\x8c\x08\xca\xf2\x85\xc5\xc2\xb0\x71\x85\xb4\x30\x6c\x4b\x3f\x92\xa0\xb3\x31\x01\x37\x71\xcb\x47\x5e\xe4\x97\x67\xbe\xaa\xcd\x3e\x52\xea\x6b\x5d\x93\xa6\x40\xdf\x18\x8d\xe5\x3d\xb2\x82\x6c\xd9\x52\x47\x72\x2f\x5d\x51\xcb\xe4\xea\xc2\x2a\x86\x2b\x4c\xb0\x06\x55\x6a\x23\x67\x13\xa0\x79\x6d\x96\x33\xf4\xd6\x33\xca\x32\x06\xcf\x8a\x7d\x33\x82\x27\x8f\xdd\x63\x18\x71\x25\x93\x59\x51\x4d\xc7\xc9\x93\xd1\x5c\x47\x61\xec\x1a\x3b\x1f\xd6\xd6\x4f\x1c\x45\xc4\x44\xec\x0f\x2c\xda\xe9\xc9\x5a\x8e\x1b\x30\x19\x1f\xc5\x56\xdc\x04\xe3\xae\xd8\xdb\x6e\x5b\xfe\x09\x8c\xca\x96\xdd\x98\x04\xd7\x8b\x20\x1b\x02\x4b\x3e\xc9\x2f\x56\x47\xf4\xef\xed\x04\x85\x4b\x14\x30\xc4\xe5\x88\x99\x52\x41\x55\xdb\xcf\xc3\x20\xa0\x4c\x20\x17\x8c\x17\xca\x61\x3d\x7e\x7f\x16\x75\xa4\x04\x65\x69\x5c\xd4\x6d\xa0\xa8\xdf\xf4\xab\x68\x63\xe7\xde\xea\xf9\x6e\x12\xe2\x67\x4e\xc9\x28\x03\xf6\x91\x72\x59\x79\x95\x5b\x58\x8f\x0b\xe8\xa3\xe2\x81\x2e\x39\x4a\xa7\x4a\x00\x98\x1f\x4a\xa4\x65\xb6\xe8\x41\xb7\xb9\xe7\x48\x91\xa6\x2f\xb0\x71\xb6\x34\x29\x6a\xb4\xca\x58\x9b\xda\x30\x79\xd3\x22\x8f\x5c\x15\xde\xc7\xe6\x9f\x05\xe4\x6b\xd3\x08\x3b\x94\x8c\xf2\x29\xbb\xc2\x60\x1f\x2e\xcf\x55\x34\x95\xa8\xf6\xeb\x8f\xe6\xc1\xf1\xb2\xf5\x38\x57\x4d\xd2\x4b\x12\xa1\x40\x53\xca\xf0\x37\x94\x1d\xf2\xbe\xeb\x52\xce\x34\x30\x80\x63\xec\xe1\xe2\xe6\xb0\x9d\x6a\x33\x1a\x17\x85\x90\x23\xd7\xfb\xbb\x22\x6b\x2f\xad\x28\x93\xa0\xf1\x73\xac\x04\x4e\x1c\xa8\x56\xb7\x53\x3a\x90\x98\x57\x53\xce\x75\xd1\x11\x02\xb0\x60\x46\x16\xa0\xa5\x1b\x66\x82\x91\xe7\xda\x79\xa1\x20\x81\x80\x29\xf4\x7e\x9c\x09\x14\xd5\xd6\x9f\x40\x9f\xcb\x69\x96\x06\xc9\x73\x65\xae\xcf\xb2\x14\xca\x9f\x51\x5a\xc1\xc9\x5c\x21\x83\xbb\xd4\xe7\x83\x84\x50\x01\x0b\x09\x42\x3b\xb9\x2c\xa4\x2a\x65\xe2\xb2\xd5\xb1\xab\xd2\x8a\x9d\x6c\x49\x9f\x2c\xe9\x61\xb7\x3a\xac\x76\x87\xb2\x3c\x24\xf8\xad\x92\xd5\x79\x0c\x27\xcc\xc6\x1a\x79\x63\x39\x69\x73\x8d\x08\x4c\xa3\x3d\x96\xe5\x58\x52\x22\xe5\x62\x29\x51\x7c\x4c\x60\x26\x14\x13\x2d\xdf\xe2\x42\x9d\x6d\xcf\x94\x54\xf9\x30\x08\x30\x99\x9a\xd4\x0d\x39\x62\xf5\xa7\xa5\x51\xfe\xc0\xb3\xbf\xba\x44\xd9\x14\x12\xcc\x61\x94\x67\x59\x09\xd6\x3b\xa3\x6f\xab\xcc\xbc\x5d\x8d\xf6\x36\x24\xd7\x83\x91\x45\xae\x72\x19\xe5\x70\x2b\xd6\x69\xd4\xaa\x3c\x92\xeb\xb3\xb4\x8e\xa3\x0e\xfe\xdf\x03\x39\x73\xe1\xd7\x40\xb2\x2a\x5b\x77\x6c\x3d\x4e\xa7\xcb\xa0\x72\xe7\xea\xeb\x04\xf6\x8a\x37\xcb\x66\xb6\xd2\x7a\xf1\xa3\xea\xa2\xfb\xd5\x8b\xee\x12\x24\x2b\x03\x0f\xe6\x87\xfc\x2a\x55\x36\xce\x51\xf8\x1d\x51\x5e\xcf\xb6\xe4\xb0\x6d\x40\x19\xd8\x36\x97\x73\x1b\x70\xec\x63\x0f\x32\x69\x79\x6c\x97\x31\xd8\x76\xd9\xd2\x1a\x14\xb1\x2f\x2f\x59\x72\x0c\xc3\xbe\xd4\xf7\x5c\xa7\x3a\x52\xf9\x4f\x60\xfb\x18\x07\x19\x4a\x88\x50\x7f\x4b\xae\xaf\x60\xd7\x8f\x7b\x65\x54\xfc\x86\x23\x03\xab\xba\xf8\xf5\x03\xa2\xe6\x97\x35\xb6\x3a\xfa\x1a\x60\x56\x88\xcc\xcb\xe7\x59\x55\xf9\x66\x25\x4c\x91\xb1\x40\xe2\x67\x1d\x4d\x69\x9a\x03\x65\xa1\x23\x43\xf8\x82\xd5\x62\x49\x16\xf1\xad\x3f\xe8\x09\x48\x2e\xce\xdf\xee\x93\x52\x56\xdf\xf1\x93\x74\x2d\xf8\xbb\x67\x27\x52\x0c\x32\xe4\x50\x96\x5c\xcb\x90\xf3\xe0\x2c\x04\xcc\x5d\xdb\x63\x39\xc6\x64\xd6\x8d\x6b\x1c\x8c\x7a\x76\xd9\xfd\x2e\x44\x6c\x61\xc3\xea\x3d\x9c\x22\x80\x89\x8b\xbe\x16\xa0\x4f\xa0\xc7\x51\x7d\x2c\x8b\xa7\x0b\xf2\xd5\xec\xda\x42\x07\xad\xa8\x5e\xd2\x2c\x63\xd7\x48\x1b\x55\xf7\x95\x48\x5f\x84\xfe\x18\x31\x49\x4a\x25\x9a\x00\x26\x00\x41\x67\x66\x4e\x7a\x83\xd3\xc8\x97\xdb\x27\xd3\xe8\x76\xf5\x44\xa2\xbb\xda\xac\x82\xec\x3f\xa9\xf7\x7d\x15\x1d\x40\xd5\x55\x77\xaa\x13\x18\x2f\x80\xc3\xb0\x40\x0c\xc3\x8e\xe2\x10\xbe\x20\x02\x7e\xd5\x11\x22\xcc\x53\x56\x03\x98\x1b\x08\x25\xda\x52\xe4\xba\x20\x70\x13\x03\xbe\x01\x8e\x07\x43\xae\x14\x2f\x24\xe0\xea\xd7\x73\x9d\xb6\xf0\x11\x11\xa9\x8f\x7e\x2a\xe9\xa6\x08\x1d\x87\x00\x54\x7f\x1d\x84\x81\x64\x91\x80\xcd\x78\xb3\x37\xda\xd5\xe7\x29\x9c\xd7\x94\xc5\xa4\x7b\x2e\x11\x63\xea\xfe\x3d\x29\xab\x0d\x5f\x57\x92\x9b\x9b\x03\x88\x19\xc2\x5a\xc0\x3f\x97\xd6\xa7\x1a\x69\x42\x3d\x8f\x7e\xc1\x64\x1a\x4d\x2c\x2a\xdf\x93\xcf\xcd\xcd\x0d\xbf\x4b\xcf\x61\xc8\x7e\x00\x72\xc7\xfc\x9e\x36\xbe\x5e\x1d\x09\x30\x82\xc4\x1d\xc5\x82\xe1\x3e\x28\x3d\x8f\x81\x94\xe3\x77\xa6\x09\x6b\xae\x30\xd9\x16\xba\x32\xc1\x45\xee\x73\x69\x26\x61\xdd\x46\x71\x1c\xc0\x1c\x20\x3f\x10\x8b\xe7\xf2\x5d\x2a\xb6\x74\x7d\x19\x0f\x3d\xc1\x01\x64\x99\xf5\x93\xd8\x74\x12\xbe\x0e\x3c\xea\xa2\xcc\xd9\xc7\x22\xaf\xe7\x58\xd9\x64\xf7\x78\x6a\xad\x92\x1d\xaa\xb7\x70\x04\xe0\xbe\xbb\x90\x8b\x85\x87\x86\x4a\xab\x69\x59\xa1\x2e\x37\xb4\xef\xb0\x74\x83\xa9\x46\xe9\x86\x32\x78\xa1\x7a\x67\x2d\xd9\x51\x5f\x66\x88\xa1\xcc\x76\x4a\x87\xcc\xec\x2a\x70\x2c\xf9\x04\xb9\xd1\xee\x88\xaf\xd1\xd7\xc8\xab\xc5\xb9\x91\x54\xba\x79\x0e\x6e\x8c\x29\xc8\x3f\x23\x6e\x91\xff\x54\x31\xde\x9b\xe7\x00\x12\x17\xdc\x44\x21\xf8\x9b\x74\xa3\xc5\x43\xe8\xa3\x2d\x94\xe9\x45\xbf\xf9\x9f\xbf\xcb\xbe\x2f\x6e\x14\xdb\xdc\x9c\x9f\xfd\x72\x6a\xe9\xe3\x50\xf2\x39\x24\x8e\xc0\x73\x94\xef\x7f\x7c\x71\x72\xa3\x87\x7c\x77\x79\xd3\x01\x3f\xd1\x2f\x68\x8e\xd8\x73\xb0\xa0\xa1\x12\x0c\x72\xe6\x10\xf8\xf0\x2b\xf6\x43\x5f\xd2\xa0\xd7\x4d\xc1\x51\xa2\xe6\x0a\xe3\x99\x2a\xb6\x30\xc8\x7f\x9a\xf0\x99\x6d\x77\xe6\x32\x5c\xfa\xb4\xbe\x88\x7e\xa0\x00\xdc\xc0\x2f\xbc\xcd\xef\x78\x5b\x27\x8b\x35\x92\x2a\x3a\xac\x49\x03\x6e\x74\x45\xd4\x4d\xdd\xed\x9a\xdd\xab\x2f\x40\x16\xbe\x02\x1f\x83\x7e\x91\x2d\xc5\x52\xdd\xff\x19\xb4\xff\x65\x9f\x86\x2e\x1e\xc7\x51\x81\xb4\x9e\x06\xd4\xa3\xe8\x5f\x30\x12\x90\x09\xae\xdf\xcb\x59\xad\x89\xb1\x87\x6f\x91\x44\xfa\x2f\xfd\xbd\xef\x22\x58\x94\xb8\x94\x1f\xb3\xcb\x62\xc8\x1b\x28\xd4\x77\xf5\x4b\x11\x33\xc8\x41\x80\x98\x8f\x39\x8f\xaa\xc7\x39\x42\x8a\xa5\x34\x5d\x90\x6b\xf0\xc1\x05\x15\xa8\x13\xe3\xa7\x95\x4e\x7a\xd2\x54\x72\x7c\x54\x7f\x83\xb9\xd1\xbb\x5c\x7c\x45\x46\x83\xe2\xb9\x12\xa1\x64\x17\x40\x16\x1d\x9f\x91\x2f\x20\x2f\xf6\x6a\x71\x49\x6b\x3d\xf1\xb6\x95\x5e\x56\xa0\xca\xa2\x62\xb4\xa2\xdb\x0a\x4c\xa0\x68\x08\xc6\xea\x6d\xf4\x52\xff\xf1\x3a\x32\xc9\x7f\xfe\x78\xbd\x65\x8e\x38\x13\x22\x90\xd0\xb3\xb3\xcd\x97\x2e\x5a\x4f\xdf\xe7\x62\xa9\x9a\xd0\xad\xb7\x8b\xcc\xcf\xa0\x67\x2c\x82\x6a\x00\xd8\x1d\x02\x8f\x4e\x47\x1c\x93\xdb\x51\xb7\xd3\x4b\x3e\xe8\x13\x2b\x19\x48\xc9\xb7\x95\x4e\xc3\xa8\xf2\x25\xbe\x63\x0e\xd2\xca\xe1\x7f\x4e\xa7\xe0\x0a\x93\xdb\xe4\x75\xec\x66\x81\x56\xa6\xb5\x2d\xed\xd9\xce\x4b\x82\x6c\xce\x2d\x0f\x39\xcd\x0a\xae\x89\x7f\x27\x20\xd3\x14\xa3\x62\xda\xaf\x0d\xb8\x39\x5e\x59\xd2\xad\xad\xca\xdf\x46\xf9\xf2\xb7\xb6\xad\xfc\xad\x98\x4a\x2a\x3f\x2e\xe4\xfb\x45\xd7\x30\xdd\x6a\xe9\xfd\x1a\xf1\x23\xb0\xf0\xf4\x0a\x58\xdd\x45\x4b\x62\xa0\x2a\x35\x00\x80\x1f\x7a\x02\x8f\x3c\x4c\xac\xe7\x95\x93\x42\x5a\x73\xcf\x67\x1b\x18\x8b\xf7\x56\xc2\x02\xe7\x98\xd8\x5a\x46\x88\x57\xb7\x51\x73\x18\x53\xea\x21\x48\x2c\xdf\xbf\xb6\xa7\x8c\x86\xc1\x10\xb4\x10\x71\x03\x8a\x89\x28\x9e\xdc\xe2\x33\xfa\x65\x04\x3d\xef\xfe\xd3\xb9\x9a\xd1\x2f\x52\xe1\x97\x4f\xa6\xaa\xc5\x3d\xa7\x22\x68\x80\x9d\x25\xf5\x02\xd4\xf7\xa5\xa1\x20\xd5\x93\x40\x6e\x72\x50\x45\x6b\x4f\x05\x40\x47\x7c\xec\x2c\x74\x5d\xde\xa0\x3c\x16\x99\xa2\xad\x76\x5d\x16\x67\x2e\x50\x70\xff\x60\x58\x2e\x8f\x90\x3e\xed\x4a\x46\x8e\x60\x12\x8e\x98\x18\x29\xab\xb1\xac\x4d\xb9\x5f\x59\x7c\x8e\x5d\x57\x15\xf9\x84\x5c\x50\x5f\x1b\xa3\xb1\x39\xe2\x50\x65\x9f\x88\x48\xf5\x47\x06\xaf\x8f\x38\xd7\x81\x00\x20\x18\x24\x1c\x8b\x7c\x16\x37\x7d\x96\x4f\x47\x3e\x4b\xe6\x52\x98\x4f\xfc\x83\x54\xb1\xd1\xad\x91\x16\x54\x7a\xa4\xd0\x75\x91\x5b\x09\x2a\x62\x8e\xd7\xb2\x53\x75\xc3\x72\x26\x31\x9f\x92\x5c\x62\x25\xf6\x9a\xa0\x26\xfa\x75\x50\xfe\x4d\xa5\x15\xef\x8d\x72\x59\x32\xd3\x7c\xda\x4b\xb1\x8a\xb3\x9c\x4b\x70\x3e\x53\xec\xaa\xa9\x0d\x8e\x1d\x91\x0f\xa2\x15\xb1\xb7\x0a\xf8\x7a\x98\xb7\x33\xbb\xc3\xda\x68\xc9\x18\x75\x76\x20\xfa\x2a\x18\x74\x56\xdb\x82\xa7\xba\x0f\x80\x11\xb3\x4e\x18\xf5\xd5\xe2\x8f\xa9\x9b\x97\x1a\xe9\xf3\xc7\xdf\x3e\x9b\xe0\xc5\x08\xa3\x98\xc4\x0f\xc5\x6a\x19\x36\xf8\x5e\xbc\x36\x83\x7c\x34\x43\xd0\x45\x6c\x34\xc1\x9e\x40\x85\xda\xdf\xf4\xc9\xac\xf1\x6b\xd5\x18\x8c\x21\x97\xee\xbf\x0e\x2d\xe8\x92\x4e\x47\xad\x3b\x25\x08\x68\xb8\xf7\x64\x3e\x7b\xe9\x45\x29\x5e\x92\xf7\xf4\xb8\x91\xb3\x4b\xe3\x7c\x5b\xb5\x60\x8b\xcf\xe6\x47\x9d\x2f\x8a\x55\x17\xd9\x27\xe2\x89\x9f\xf4\x50\xcb\x9b\x6f\x8e\x57\x2d\x05\x21\x45\xb4\x20\x8f\x51\x8b\x16\xea\xfb\xb3\x6b\x81\x93\xea\xb1\x6c\xea\x02\xd6\xf6\xfd\xde\x2e\xce\xe9\xd4\xcc\x3a\x2d\x39\xc5\x91\xb9\x17\x02\xb4\x8e\xc6\x7c\xde\xe5\x07\x82\xa0\x83\x69\xb7\x3f\x9d\xed\x4d\x07\x86\x7b\x53\x38\xe1\x64\xf4\xd9\x1f\xb3\x09\xeb\x76\xfb\xc1\x84\xdc\xce\xba\xa6\xe5\x96\x5e\x88\x01\x5a\x9c\xcd\x9d\x36\x74\x1c\xd1\xee\xed\xf7\xd1\xa4\xef\x1e\xb6\xbb\xfd\xee\x51\x7b\xd0\xeb\x1d\xb4\x0f\x07\xfb\xfd\xb6\x3b\xd9\xdf\x75\xfa\xdd\xfe\x9e\xd3\xdf\xb7\x40\x89\x2e\xcb\x00\xad\x71\x6f\x30\x70\x8f\x8e\x7a\xed\xee\x21\x1a\xb7\x07\x83\x83\x7e\xfb\x10\x39\xbd\x36\x1a\x77\x77\x07\xce\xfe\x51\x7f\xb7\x37\x36\xfb\x87\xcc\x1b\x82\xd6\x84\xd2\xb6\x0d\xdf\xce\x2d\xe4\x1d\xe8\xf8\xa8\xe3\x50\x7f\x38\x18\xec\xb6\x72\xee\x96\xf5\xe4\x94\x31\xfd\xee\xed\xa1\x47\xa6\xdd\xdd\x1e\x47\x47\x77\x35\xa6\x8f\xba\xfd\xbd\xfe\xfe\x1e\x6a\xc3\xc3\x43\xd8\x1e\x0c\x26\xe3\xf6\xe1\x60\xaf\xdb\x46\x6e\xb7\xd7\x45\xe3\xfd\xb1\xb3\xe7\x54\x4d\xdf\x75\xf6\xe0\x61\xff\xe8\xb0\x3d\x46\xee\x41\x7b\xd0\xef\xa3\xf6\xe1\xd1\xe0\xa0\x3d\xd9\x9f\xb8\x70\xff\xa8\x7f\xd4\x9f\x4c\x8a\xd3\x1f\x43\x16\x4d\xbf\xef\x4f\x1c\xd8\xed\xf6\xc5\xd1\xdd\x01\x9f\x76\x38\x2b\x9b\x7e\x7c\x8a\x28\xef\x57\x17\xcf\x23\x81\x96\xdd\xa9\xb7\x9e\x0c\xb3\xb9\xa6\x89\x6f\x65\xc6\x8e\xf4\x93\xfa\x91\xbc\xf0\x35\xf2\x65\xd4\xe2\x3e\x1f\xc3\xec\x8d\xb5\x89\x57\x9d\x1d\xea\x16\x2d\xf2\xbb\x35\x4e\x98\xb6\xae\xae\x2f\xcf\x2e\xde\x64\x7d\x0f\xab\x9d\x99\xf4\xf8\xf9\xea\xdd\x45\xee\xfa\x8e\xc8\x69\x2f\xa4\x3b\x2b\x1d\x88\x28\x7c\xa3\xbe\x4a\xb1\x59\x74\x3f\xe3\x60\x97\x6a\xa2\x4c\xd2\xb2\x83\x55\xb9\x13\x99\x2a\x5e\x37\x8a\xcf\xcb\x99\x43\xbb\x08\xba\x23\x0f\x09\x81\xd8\xe8\x2e\x44\xf9\x69\x2a\xea\x4a\x86\xf3\xee\x72\xd1\xa4\xea\xdf\x2a\x2c\x8b\x4c\x59\x2e\x4c\x34\x12\xdf\xcb\x24\x50\x49\x7d\x63\xfc\xa3\x2d\xad\x6c\xf8\xa6\x03\x03\xdc\xa1\x01\x22\x7c\x86\x27\x42\xf2\xf6\x4e\xc0\xe8\x04\x7b\xc8\xb6\xba\xa0\x15\x39\xf0\xed\x4c\xa3\x15\xee\xc2\x2c\x9b\xb3\xec\x60\x99\xf7\x23\x4c\xa6\xfc\x4a\x47\x4b\x20\xb0\xd5\xeb\x1a\x82\x20\xba\xb3\x26\x77\x61\x5d\x75\xec\x4c\x5f\xe4\xb8\x93\x81\xa3\xae\x11\x03\xad\x57\xef\x2e\x2e\x4e\x5f\x5d\xbf\xbb\x6c\xbf\x7d\xf3\xf6\xba\x9d\x69\x12\x5d\x1e\x06\x5a\x57\x0b\xe2\xcc\x18\x25\x34\xe4\x00\x2a\x2d\x0d\x30\x07\x84\x8a\xb4\x06\x5d\xc7\xe6\x21\x5f\x10\xe7\x85\x14\x0c\xc5\x8b\x3f\x72\xb7\x8b\x81\x56\x0f\x7f\x3c\xc3\xfe\xdd\x1b\x87\x9d\x84\xe7\xfb\x3d\xf8\xe1\xeb\xd9\x3f\xee\x5e\x5e\xdf\x5d\x5c\xc2\x84\x4a\x67\x3a\xd6\xfd\x6b\x88\xd8\xa2\x06\xa5\xfa\x1b\xa2\x54\x7f\x29\xa1\xfa\x16\x3a\xfd\xc7\x60\x80\xd7\xea\x98\xb5\xb4\xed\x02\xc8\x38\xca\x64\x7a\x86\xe0\x03\x81\xd1\xcf\x47\xab\x70\x8e\x8e\xe5\x44\xe5\x54\x5c\xdd\x82\x01\x03\x3c\xd2\x21\xcf\xe8\x04\xf2\x10\x14\x30\x18\xae\x30\x5e\x7a\x58\xc0\xa1\x5e\xe8\x13\x6d\x7a\xca\x91\xa2\x50\x3e\xd8\xc6\xee\x76\x07\x5c\xd9\xda\xa9\x9c\x97\x39\x9a\xd4\xc2\x94\x3c\x8f\x32\xd1\x8e\x47\x43\x77\x14\xe5\x4b\x58\xfc\x56\x1f\x1a\xec\x80\x5f\x75\xde\x42\x2f\xe4\x10\x60\x17\xbc\x00\xbd\xfe\x6e\x29\x57\x78\x1f\x4f\xde\x84\x8b\xf1\x19\x3b\x25\x5f\xd9\x31\xf2\x0f\xfa\x83\xe9\xdd\xed\x2d\x3e\x99\xc7\x5c\x91\xbf\xaf\xd2\xc6\x09\x83\xee\x60\x23\x9c\x70\xb0\x8c\x11\x0e\x2c\xfb\xa5\xce\xa5\x97\xc9\x64\xac\xf7\x53\xdb\xa6\x74\xf0\x78\x13\x4a\x53\x5b\x2a\x2c\x86\xdd\x17\xdb\x3d\xfc\xcb\xae\x1b\xfe\xf6\xe9\x6c\x3e\xdf\xfb\x34\x3f\xf7\x16\xdf\x7a\xfe\x9b\xcb\xdd\x9f\x17\x77\x17\xdb\x4a\x34\x4c\x68\x48\xdc\x8a\xcd\xff\xe9\xdd\xc1\xb4\x3f\xdd\xff\xe9\xda\xfd\xf0\xcb\x07\xd8\xbf\xe5\x3f\x1d\xf6\x6f\x7f\x3d\xd9\x5d\xc4\x94\xc9\xdf\xdd\x6a\x15\x8d\xbd\xcd\x48\xc6\xde\x52\xc1\xd8\xb3\x90\x25\xdd\xc6\x73\xc4\xf0\x64\x01\x7e\xfe\x78\xad\x6f\x84\x1d\x82\xcb\xc8\x1b\x01\x30\x14\x33\xca\xf0\xb7\xf8\xba\xa8\x5b\x44\xea\xd1\x67\xf7\xc3\xec\x74\xf6\xc5\xff\xfd\x65\xf0\xf1\xfd\xe4\xac\xef\x5d\xa0\xdb\xc0\x1d\xfc\xe3\x24\xa6\xcf\x91\xd4\x65\xaf\x28\x99\x78\xd8\x11\x35\x68\xb5\xbb\xbf\x11\x5a\x99\x60\xec\xb4\x32\x5b\x98\x2c\xa4\x0f\x27\x69\xc9\x83\x39\x80\x9e\xb2\x8d\xd4\x19\x9a\x52\x3a\xec\xdf\x7e\xea\x7e\xc0\xa7\xb7\xdf\x6e\x7f\x7f\xf5\xed\xe3\x7b\x74\xd6\xa7\x9f\xd0\xcc\xdd\x3d\x8d\xc8\x50\xbc\x89\xd5\x36\xf5\xa3\x8d\xcc\xfc\x68\xd9\xc4\x8f\xac\x3c\x92\x5e\xa4\x8f\xb2\x83\x16\x96\x1c\x9d\x9e\xcf\x5f\x1f\x7d\x7e\xfb\xeb\xa7\xfd\x4f\xd3\xd9\xe4\xed\xd1\xf4\xcd\x25\xff\x69\x7e\xfa\x31\x99\x6b\x6d\x61\xf1\x78\x33\x36\xb5\xa0\x1a\x33\xb9\x61\x04\x48\xeb\x80\x4b\xbf\xe9\xdd\xab\xb7\xed\xd3\xdf\xdb\x47\xc3\xe8\x3a\x12\xb9\x85\xf4\xa5\x23\x69\x1b\xf4\x55\xb4\x23\xdd\x07\x03\xdc\xee\xe1\xaf\xdd\x5d\x8f\xb8\x9e\x7f\xd7\xbd\x9b\x38\x07\x1c\x0b\xb8\xc7\xbd\xcf\xf3\x43\xd3\x09\x99\x18\x3f\x99\x2f\xe9\xd0\x9b\xee\xb9\x87\x87\x77\x5d\x8f\x39\xee\x7c\x30\x3d\x80\xde\xf8\x80\x7b\x93\x29\xf9\xbc\xeb\xce\xc6\xfc\xf3\x5f\xfe\xdf\x5f\x4f\x7f\xbf\xbe\x3c\x06\xff\xa5\x67\xdc\x51\x18\xbf\xc0\x2e\x22\x42\xae\x99\x19\x21\xc0\x1c\x6c\x0f\xba\x83\xed\xe7\x8a\x16\xea\xcf\x57\xe7\x1f\xae\xae\x4f\x2f\xaf\x34\x31\xe4\x47\x95\xe9\x4e\x16\x16\xa4\x80\x54\xfb\xde\x74\x8f\xb2\xbd\xee\x1c\x87\xdd\x03\x8a\xe4\xb2\xcd\xd8\xad\xd3\xdf\x77\xa7\x13\xf1\xb9\x07\x9d\x6d\x53\xc9\x46\xc9\x63\xd5\xab\x72\x12\x86\xbc\xfd\x5b\x85\x3c\xb9\xe6\x1f\xd9\x62\x9f\xf0\xbb\x71\x9f\x5f\xf8\xaf\x3f\xef\x8d\x7f\x0f\x4e\x0e\x5e\xc1\xd6\xd6\xff\x05\x00\x00\xff\xff\x91\x27\x31\x4c\x3f\xdb\x00\x00") +var _connector_mgmtYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x7b\x73\xdb\xb8\xb5\xf8\xff\xfe\x14\xf8\x29\xbf\x8e\xdb\x7b\x23\x59\x92\xe5\x97\xe6\xa6\x33\x8e\xed\x64\xbd\x9b\x38\x59\xdb\xd9\x6c\xda\xe9\xc8\x10\x09\x49\x88\x49\x80\x06\x20\x25\x4a\x7b\xbf\xfb\x1d\x00\x7c\x00\x24\x48\x51\xb2\x62\x3b\xbb\xe2\x4c\xbb\x31\x09\x1c\x1c\x1c\x1c\x9c\x37\x20\x1a\x21\x02\x23\xdc\x07\xbb\xad\x76\xab\x0d\x9e\x01\x82\x90\x0f\xc4\x04\x73\x00\x39\x18\x61\xc6\x05\x08\x30\x41\x40\x50\x00\x83\x80\x7e\x01\x9c\x86\x08\x9c\x9f\x9e\x71\xf9\xea\x96\xd0\x2f\xba\xb5\xec\x40\x40\x0c\x0e\xf8\xd4\x9b\x86\x88\x88\xd6\xd6\x33\x70\x1c\x04\x00\x11\x3f\xa2\x98\x08\x0e\x7c\x34\xc2\x04\xf9\x60\x82\x18\x02\x5f\x70\x10\x80\x21\x02\x3e\xe6\x1e\x9d\x21\x06\x87\x01\x02\xc3\xb9\x1c\x09\x4c\x39\x62\xbc\x05\xce\x47\x40\xa8\xb6\x72\x80\x18\x3b\x0a\x6e\x11\x8a\x34\x26\x29\xe4\xad\x67\xa0\x11\x31\x3c\x83\x02\x35\x9e\x03\xe8\xcb\x59\xa0\x50\x36\x16\x13\x04\x1a\x1e\x25\x04\x79\x82\xb2\x41\x38\x0e\x45\x33\x6e\xd9\x9a\xc3\x30\x68\x80\x11\x0e\xd0\x16\x26\x23\xda\xdf\x02\x40\x60\x11\xa0\x3e\x38\x49\x3a\x80\x2b\xc4\x66\xd8\x43\xe0\x55\x80\x90\x00\x6f\x21\x81\x63\xc4\xb6\x00\x98\x21\xc6\x31\x25\x7d\xd0\x6e\x75\x5a\xed\x2d\x00\x7c\xc4\x3d\x86\x23\xa1\x5e\x2e\xe8\xaf\xe7\x73\x89\xb8\x00\xc7\xef\xcf\x25\x9a\xa1\xfa\x00\x52\x44\x79\x6b\x8b\x23\x26\x07\x91\x58\x35\xc1\x94\x05\x7d\x30\x11\x22\xe2\xfd\x9d\x1d\x18\xe1\x96\x24\x36\x9f\xe0\x91\x68\x79\x34\xdc\x02\x20\x87\xc0\x5b\x88\x09\xf8\x6b\xc4\xa8\x3f\xf5\xe4\x9b\xbf\x01\x0d\xce\x0d\x8c\x0b\x38\x46\x8b\x40\x5e\x09\x38\xc6\x64\xec\x04\xd4\xdf\xd9\x09\xa8\x07\x83\x09\xe5\xa2\x7f\xd8\x6e\xb7\x8b\xdd\xd3\xef\x59\xcf\x9d\x62\x2b\x6f\xca\x18\x22\x02\xf8\x34\x84\x98\x6c\x09\x38\x8e\x09\x40\x60\x68\xad\xcb\xf5\x3c\x42\xbc\xd8\xbf\xd1\x70\xb5\xae\xdd\x10\x9c\x04\x53\x2e\xd0\x12\x1d\xe2\xf5\x75\xb6\xdf\x8a\xa0\x98\x28\xfc\x9f\xc9\xff\x01\x67\xb7\x67\x5b\x5b\x00\x34\xe4\x32\xec\xd8\x6c\xba\x33\xeb\x34\xfa\x0a\xee\x18\x09\xfd\x0f\x00\x12\x82\xe8\xa7\x59\x82\x08\x90\x7b\x91\x41\x89\xc8\xb9\xdf\x97\xfd\x7f\xd3\xec\xfa\x16\x09\xe8\x43\x01\xe3\x56\x7c\x1a\x86\x90\xcd\xfb\xe0\x12\x89\x29\x23\x5c\xed\x96\x98\xb3\x41\x68\xb7\xb5\x26\x57\xa3\x3d\x43\x3c\xa2\x84\x23\x03\xdd\x46\xb7\xdd\x6e\x64\x7f\x02\xc9\xee\x02\x11\x61\xbe\x02\x00\x46\x51\x80\x3d\x85\xfc\xce\x67\x4e\x89\xfd\x15\x00\xee\x4d\x50\x08\xf3\x6f\x01\xf8\xff\x0c\x8d\xfa\x60\xfb\xd9\x8e\x47\xc3\x88\x12\x44\x04\xdf\xd1\x6d\xf9\x4e\x6e\xfa\xdb\x46\x67\x6b\x5e\xbf\xe5\xe7\x92\xae\x5d\x91\xf3\xaa\x16\x6e\xe7\x16\x8e\x6e\xe1\x20\x7b\x2f\x64\xa7\x9d\x7f\xdb\x2f\x06\xd8\xff\xdf\x98\x1e\x11\x64\x30\x44\x22\xde\xef\x7a\x6d\x35\xab\x15\xba\x6c\x39\x31\xbf\x9e\x20\x80\x7d\x40\x95\xc4\xcc\x3a\x01\xd9\x69\xab\x9c\x74\xf2\x73\x1f\x70\xc1\x30\x19\xa7\xaf\x31\xe9\x03\xc9\xba\xe9\x0b\x86\xee\xa6\x98\x21\xbf\x0f\x04\x9b\xa2\xfa\x3c\x99\x6d\x52\x00\x38\xf2\xa6\x0c\x8b\xb9\xd9\xf2\x25\x82\x0c\xb1\x3e\xf8\x27\xf8\x57\x09\xdf\xa6\xb0\x24\xa8\x97\xf3\xf3\xd3\x3c\xe7\xbe\x46\x02\xc0\xdc\x7c\xa5\x16\x49\xe9\x64\x51\x69\x61\xeb\x47\xe2\xda\x86\x93\x6b\xad\xc9\x37\x72\x5d\xd1\x57\x18\x46\x81\x89\x68\xf2\x58\xdd\xce\x74\xb3\x62\x2b\xf7\xd0\x09\xd4\x1d\x17\x90\x46\xd9\xb6\xb9\x2e\xb0\x1c\x08\xa1\xf0\x26\x52\x5d\x48\x76\x94\xfc\x83\x94\xe4\x8f\x49\xda\x6b\x77\x1e\x87\xa4\x67\x8c\x51\x56\x9f\x94\xbd\x76\x67\x55\x02\x66\x5d\x4b\xc9\x76\x3c\x15\x13\x20\xe8\x2d\x22\xd2\x20\xc0\x64\x06\x03\x63\x7b\x37\x7a\xed\xde\x0f\x42\xa4\xde\xea\x44\xea\x2d\x22\xd2\x05\xcd\x78\x29\xc7\x63\xe8\x2b\xe6\x82\x67\x04\xdb\x7b\xac\x8d\xba\x24\xc1\xf6\xda\xed\x55\x09\x96\x75\x2d\x25\xd8\x07\x82\xbe\x46\xc8\x13\xc8\x07\x48\xe2\x05\xa8\xa7\xac\x2a\x7f\x69\x7d\xb5\x8c\xf9\xb1\x66\x51\xcf\xcb\x2c\x14\x08\x02\xcc\x85\xd4\x73\x36\x33\xf0\x2a\x33\x65\x51\xa7\xa2\xf6\x95\x28\xbb\x16\x22\x6b\xb9\x13\xc1\xb1\xb1\x08\x0b\x9b\x73\xfc\x6d\x99\xe6\x94\xf9\x88\xbd\x9c\x2f\x33\x00\x82\xcc\x9b\x34\x9e\xbc\x22\x7b\x83\xb9\x28\x17\x89\x0b\x56\x6a\xa3\x3b\xea\xe9\x8e\x8d\x28\x5c\x28\x0a\x73\x76\xfd\x92\x16\x7d\x22\x1c\x23\xe9\xf1\x2e\x92\x8e\xf7\x10\x8c\x1e\x43\x50\x20\x13\x4b\x60\x8a\xc5\x13\xf5\x59\x05\x47\xbe\x64\x5b\xc6\x25\x0b\x2b\x5b\xba\x05\xa0\xf4\x03\xee\xa6\x88\xcd\x0d\xfa\x6a\xa7\x04\xf2\x39\xf1\xca\xa8\xfe\x1e\xb1\x11\x65\xa1\xb2\xfc\xa0\x8a\x3e\x00\x4c\x00\x24\xba\xd7\x84\x51\x42\xa7\x1c\x84\x90\x10\xc4\xb6\xaa\xb9\x4d\xbb\x27\x43\x4a\x03\x04\x89\xf1\xc5\xe1\x90\x80\xc4\xca\x7c\x49\x7d\x83\xc0\x25\x61\x19\xc3\x51\x75\x6e\x8e\xea\xad\xe1\xde\x18\xb5\x24\xe0\xa5\x46\xd2\xde\x21\x65\xfb\x23\xed\xa5\x17\xaf\x74\xa7\xd4\xb3\xe4\x2d\x20\x8d\x2a\xe7\xae\x4c\x7d\x74\x1f\x59\x7d\x94\x4b\x43\xcf\x43\x91\x40\x96\xf1\xfc\x63\x08\xc0\x5e\xbb\xad\xd6\x05\x53\xb2\xba\xb6\xc8\x83\x28\xa5\xd3\x6f\x52\x4b\xa8\x96\x5a\x20\xf2\x4c\x22\x1a\x94\xdb\xe8\xd7\x8d\x6f\x56\xcb\x37\xbb\xce\x7c\x7b\xe4\x4b\x99\x41\xa7\xcc\x43\xc0\xa7\x88\x93\x6d\xa1\xfd\xb3\x8d\x4d\x92\x63\x2c\x02\xa6\x65\x66\x89\xd6\xf6\x49\xd4\xc4\x56\xd2\x75\xbc\xb0\x7b\xd8\x19\xd2\xec\x2e\xc2\xf9\x83\x79\x5f\x4f\xda\x37\x5a\xd6\x2f\xda\xb8\x44\x1b\x97\xe8\x71\xa2\x43\x7c\xe7\xdf\xd5\x99\x8b\x05\x9b\x11\xfb\x8d\x87\x10\x69\x66\x4c\x29\x2f\xd0\x72\x89\x00\x97\xf8\x72\x37\x79\x9a\xb2\xa3\x66\x64\x7e\x13\x94\xdf\x18\x7e\x75\x88\xb4\x09\xca\x2f\x45\xb0\x7b\x89\x5d\xdd\x34\x40\x02\x7d\x4f\x59\xa8\x47\x28\x15\x87\xa7\xea\xf3\x22\x89\x58\xda\xca\x2d\x14\x9f\xca\x46\x71\xcc\x61\xe3\xee\xfe\x61\xa5\x9e\x5e\xe0\x7b\xc8\x3e\x0b\x40\x95\x04\x54\x56\x51\xa2\x46\xc1\x17\x2c\x26\x80\x47\xc8\xc3\x23\x8c\x7c\x70\x7e\xfa\x23\x4b\xc2\xfb\x11\x31\x0f\x60\x45\xa9\x18\x49\x0d\xf3\x3d\x85\xa2\x1a\xa0\x54\x26\xbe\x97\x5f\x17\x89\xc4\xb2\x46\x8b\x63\xd1\xa7\x50\x40\x20\xa8\x46\x22\x57\xb4\x23\x79\x69\xab\x82\x4d\x4c\x26\x09\x11\x1b\xa3\xa6\x82\xf2\xdf\x75\x23\xd5\x3a\xac\x4e\x87\x9f\x91\x27\x4a\xc0\x4a\x50\x4b\x42\xcd\x39\xac\x3f\x5f\xbd\xbb\xd0\xf4\x79\x0e\x2e\x5f\x9d\x80\xfd\xa3\x76\x17\x34\xd3\xba\x43\x41\x69\xc0\x5b\x18\x89\x51\x8b\xb2\xf1\xce\x44\x84\xc1\x0e\x1b\x79\xb2\xd5\x6a\xd8\xae\x3f\x44\x9f\x76\xfe\x23\x84\xc8\x37\x9e\xc0\x9f\x57\x27\x6e\x3c\x81\x1f\xc1\x13\x70\xd4\x9a\xc6\xe5\xc8\xcb\x56\x9b\x7a\x71\x15\xf3\x32\x39\x6a\xbb\xf4\xb9\x3a\x0b\x9d\xa1\x05\x6a\x6b\xde\x05\x29\x6b\xe0\x59\x30\x6b\xa4\xae\x73\x3d\xfe\x74\x29\xec\x78\xfa\x8f\x97\xca\x8e\xb9\x60\xc5\x8c\xb6\xee\xbc\x9e\xc4\xb6\x03\xd6\x0f\x99\xdf\x8e\x27\xb2\x49\x73\x6f\xd2\xdc\x06\xe5\x36\x36\x4e\x0d\x22\x6d\xd2\xdc\x4f\xcb\xcc\x59\x21\xcd\x6d\x29\xf4\x5a\x45\xc7\x39\x93\xe5\xbe\x69\xef\x3c\xb8\x3a\xd9\x6f\xcf\xee\x53\x3b\x01\x9e\xeb\xf7\xd0\x15\xc8\x4f\x33\x8d\x15\x2f\xc0\xd2\x15\xc2\x39\x62\x6e\xa4\xfb\x26\x23\xfe\xc0\xe7\x25\x12\x0e\x34\x8f\xf8\xc5\xef\x96\x3c\xe5\x97\xf5\x72\x3b\x00\x65\x07\xfd\x6c\x6f\xe8\xe1\xcf\xfa\xdd\x5f\x16\x9b\xf9\xfa\x9c\x87\x59\x76\xda\xaf\xc2\x69\xac\x6e\xfa\xa4\xe5\x5f\xcd\x18\x5e\xe2\x00\x6e\x62\x79\x1b\x3b\xb7\x0e\x91\x56\x8c\xe5\x25\x6c\xb6\x89\xe9\xad\x9a\xc7\x9a\x3e\x88\xf8\x9c\x46\xbe\x23\x46\xf7\x72\x7e\xee\xe7\xa5\xe8\xd4\x8f\xa0\x9d\xc7\xaf\x12\xa4\x0b\x5b\xd7\xcf\x75\x69\x14\xfd\x15\x33\x5d\x0f\x12\xbc\x5a\x22\x5a\x64\x8b\x0c\x3b\x4a\x17\xef\x19\x2e\xa0\x98\xaa\xfb\x51\xe2\xa9\x6f\xe4\xf2\x46\x2e\xaf\x59\x2e\x6f\x44\xb2\x8b\x66\x6b\x29\xb8\x5a\x83\x54\xce\x15\x5e\x95\xd8\xb5\xc5\xca\xaa\x2a\x89\xbc\xb0\xf5\xa6\x1e\x6b\x23\x17\x9f\x08\x91\x1e\xb0\x1e\x2b\x0d\xcc\x6e\x4a\xb1\xd6\x59\x8a\xb5\xbe\x28\xc8\x0e\xf4\x7d\x4a\x06\x59\x14\x64\x13\x16\x59\x2d\x2c\x72\x2c\xe9\xf8\x3e\xa5\x5a\x5e\x9b\x94\x84\x3e\xb6\x39\x50\x0b\x60\xd0\xdb\xa5\x5d\x96\xee\xfd\xa4\x62\x29\x36\x69\x2a\x23\xc9\x92\x65\xb2\xc9\x00\x31\x81\x02\xf0\x09\x9d\x06\x3e\x18\x22\x30\xe5\xfa\xb6\x41\x8f\x92\x11\x1e\x4f\x19\x52\x8c\xa5\xef\xe9\x33\x3d\x18\x4d\x14\x4a\x34\xdf\x69\x5a\xb5\x36\xea\xec\x8f\xaa\xce\x36\xe1\x97\x1f\xc9\xd6\x5f\xa3\xee\x92\x0a\x89\x47\xd0\x43\x3f\xb8\xd6\x5a\x32\xaf\xb8\x54\x56\x71\xd9\x5b\x8d\x96\xba\xd3\xe8\xf1\xd4\xed\x45\xba\xf4\xf5\x35\x2d\xc9\xf7\xa9\xa9\x63\x0b\xfd\x9e\x94\x76\x4d\x29\x93\x92\x64\xa1\x86\xcd\x26\x04\x66\x98\xe3\x61\xa0\xae\x13\x9e\x72\xc4\x00\xde\x28\xcd\x8d\xd2\xdc\x28\xcd\x27\xa8\x34\xf3\x75\xc8\x96\x04\x5c\xaa\x14\xb9\xa8\x36\x6b\x15\x23\x17\x44\x6e\x55\x39\x72\xda\x78\x29\xa9\xbf\xa8\x20\x99\xe4\xa0\xd6\x29\x49\xce\xf7\x59\xa6\x9e\x37\xed\xfb\x78\x15\xbd\x29\x21\x57\xab\xe9\x4d\xbb\xaf\xa5\xaa\xd7\x0d\xed\x87\xac\xeb\x4d\xa7\xb2\xa9\xec\xdd\x54\xf6\x1a\x94\xdb\x58\x0f\x35\x88\xb4\xa9\xec\x7d\x5a\x86\xc3\x2a\x95\xbd\xb6\x5e\xac\xe5\xc9\x15\x9d\xae\x7b\x56\xf7\x96\x7b\x71\x55\x75\xba\xd5\x7e\xdc\x52\x3d\x37\xb7\x0c\x67\xcf\x13\x70\x4e\x5d\x85\xc4\x85\x35\xdb\xa8\x93\x4d\x29\xf1\x03\x07\x22\x33\x1e\x34\x43\x91\xe9\xdb\x25\xcb\x89\xcd\x7e\x6e\x0f\xa4\x2c\x06\x99\xf7\x65\x1e\x3e\x77\xb6\x0e\x15\x60\x86\xf3\x0a\x7e\x62\x59\x18\xaf\xd2\xf5\x5b\xd4\xf8\x89\xcb\xc4\x9a\xc5\xc5\x99\x37\xba\x29\x2f\xde\x18\xdb\x75\x88\xb4\x62\xa8\x2e\x63\xb4\x4d\xb0\xee\x7b\x16\x18\xaf\x43\x98\xe6\x4a\x8c\x53\x90\x35\x8b\x8c\x2b\xc5\x6a\x8d\xf6\x3f\x64\xa1\x71\x79\x4c\x6d\x3d\xa5\xc6\x29\xfc\x4d\xb1\xf1\x46\x4a\x3f\x80\x94\xde\x08\x68\x17\xd5\xd6\x53\x6e\xbc\x0e\x19\x9d\x2b\x38\x2e\xb5\x79\x1d\x45\xc4\x95\xf2\xb9\x46\xfb\x4d\xd9\xf1\x46\x42\x3e\x11\x22\x6d\xca\x8e\xff\x44\x65\xc7\x46\xc4\x04\xcd\x60\xb0\xf6\x44\xf3\xd9\x0c\x06\x53\xf5\x6e\x9d\x99\x66\x3e\xa1\x4c\x80\x00\xcf\xe4\xdc\xd3\x11\x56\x4a\x40\xd7\xea\xfe\xa3\xe6\xa2\x25\xf5\xef\x99\x8f\x96\x20\xd6\x9b\x93\x2e\x40\xdc\xe4\xa5\xbf\x37\xd6\x9b\xbc\xf4\x83\x51\x6e\x63\x62\xd4\x20\xd2\x26\x2f\xfd\xb4\x5c\xb0\xfb\xe5\xa5\xb7\xb2\x51\x25\x72\xf1\x0c\xfb\xfa\x0e\xe0\x67\xfa\xff\xc1\x09\x0d\x43\x4a\xe2\x57\xea\x3f\x6f\x70\x66\x64\xa4\x82\xdf\x30\x06\x6e\x31\xf1\x8d\x3f\x23\x38\x46\xc6\x9f\x1c\x7f\x33\xff\x14\x54\xc0\xc0\xf8\x1b\x0b\x14\x26\x66\x89\xe3\x12\xe4\x88\x49\x5b\x45\x60\x93\xd4\x72\xbc\x85\x09\x1a\x89\x45\xb1\x11\x26\x02\x8d\xcd\x9a\x73\xfc\xad\x46\x2b\x85\x73\x79\x33\xf5\x41\xb1\x49\xd2\x06\x06\xc1\xbb\x91\x49\xa2\x2a\x06\x7b\xa7\xe6\x7b\x89\x46\x88\x21\xe2\x59\x99\xed\x92\x5b\xa1\x5d\x44\x01\x6a\x4f\xf8\x05\xae\x73\x12\x07\xa8\x95\x84\x8e\x1d\x52\xda\x3c\x35\x19\x07\xd8\xaf\xec\xa4\xbe\xe5\xe6\xd4\x5f\x6e\x81\xf1\xe2\xe5\xad\xc5\x03\x13\x49\xf5\xb2\x46\x06\x9e\x6f\x91\x80\x4b\xa2\x48\xbf\x10\xc4\x16\x22\xa0\x4d\x6b\x7f\x00\x2d\x39\x35\xa2\x2c\x84\xa2\x2f\xcd\x4e\xd4\x14\x38\x44\x8b\xc0\x84\xd4\x57\xee\xd6\xaa\x70\xd4\xfb\x2b\xc4\x66\xd8\x4b\x82\x26\x98\x92\x2b\x24\xa4\xb4\xe0\x55\x5b\x1b\x9b\x1b\x7b\xca\x82\xfb\x2d\xda\x94\x39\x76\x91\x03\xc7\x63\xcf\xa3\x53\x52\x29\x73\xbc\x00\x23\x22\x06\x16\x7e\xf1\x3b\x8e\x3c\x86\xaa\xd6\x2e\xed\xbb\x78\xfd\x4c\x88\xd5\xa8\xff\x86\x18\xc7\x94\x48\x56\x92\xee\xc4\x03\x49\x02\xe4\x52\x35\x6a\x6f\x80\xc6\xf1\xfb\xf3\x18\x29\x5b\x7b\x61\xf9\x71\xd6\xb1\x5f\x4e\x34\x5a\x6e\x67\xb4\x91\x93\x32\x41\xa0\x39\xa8\xa0\xfe\x9a\x1a\xb8\xf2\x5d\x79\x5e\x67\x2e\x18\xa4\xf8\xe3\xc3\x85\xfe\xf1\xc4\x4a\x7f\x4d\xae\x5c\x2e\x96\x62\xac\xe9\x0a\x19\x83\xf3\xdc\x17\xa5\x98\x8a\x3a\x3c\xb7\xa0\xe6\xdc\x97\x5a\x5a\x4b\xe7\xc6\x7c\xcf\x4d\xad\xfb\x8b\x24\x47\xf9\x6e\xb5\xcc\x82\x9f\x68\xe0\xf3\x44\xed\xab\x93\x9c\xda\x4c\xd7\x47\x3b\x25\x04\xf9\x4f\xa8\x61\x82\x73\xc2\x05\x24\x1e\x6a\xad\xc2\xa3\xa5\x62\x24\x5b\x88\x67\xf1\xaf\x86\xc4\x61\x22\xcf\x58\x97\xac\x4d\x09\x4b\x3f\xb3\x57\x51\x4b\x05\x35\xf4\x25\x1a\x63\x2e\xd8\x7c\xcd\x24\x51\xc0\x41\x02\xfc\x01\x68\xa3\x1b\x03\x96\x8c\xb8\x2e\x2a\x25\xbc\xa4\x0e\x07\x5b\x9c\x64\x1f\x17\x76\x52\xab\x71\x9c\x3f\xf8\xdc\x58\xbb\xca\x9e\xc1\x60\xea\x30\xb6\x4c\x21\x5a\x3c\xd8\x5c\x86\x6d\x52\xd6\x96\x3f\xae\x6d\xa3\x6d\xee\xeb\xdc\x7e\xae\x7f\xbe\xba\x91\xb7\x8f\x8b\x17\xd7\xa7\xa4\xce\x9f\xaa\xbb\x12\x50\xe4\xac\x1f\x8b\x2a\x88\x4c\x43\x93\xbb\x7c\xcc\x63\xee\x44\xa6\x66\x63\x08\xfa\x73\xf7\x08\x71\xd4\xc8\x34\x61\x5c\xeb\xa3\x4a\xa6\x2a\x69\x5f\x02\xd8\xbd\x00\x7a\x4b\x4a\x0b\xc4\xac\x98\xc9\xb2\xd2\x00\xaa\xa0\x1a\x88\x02\x48\x90\x71\xf2\x4f\xa7\x6f\x1b\xab\x6c\xae\x8a\x89\x37\xdc\x33\x30\x69\xb2\x82\x1e\xd6\x90\xbf\x17\x72\x57\x8a\x12\x55\x4b\xc6\xad\x16\x25\x7b\xb1\xac\x73\x02\xa0\xe0\x0f\x2c\x33\x0b\xc5\xbd\xb9\x78\xa4\xe9\xe6\xd4\x67\xa6\x75\x9b\x43\xcb\xcc\xe2\x3e\xeb\x78\x15\xf3\xab\x73\x52\xa6\x7c\x5a\x6a\x62\xb6\xdd\xb2\xb4\x9b\xe7\xb4\x4c\x96\x36\x64\x96\xbb\xac\xd3\x2d\x02\x8d\xb7\x27\x13\x48\x08\x0a\x2a\x64\x9d\x8f\x46\x70\x1a\x08\xf9\x16\x0e\x03\x54\x22\x01\xe3\x8f\x36\xc1\x4f\x11\x97\x0e\xc0\xb2\xd2\x54\x8b\x4d\x13\x36\x8d\x22\x4b\xb0\xfa\x71\x8a\xd4\x1e\x6e\xd9\x71\x20\xe7\x78\x4c\x4c\x5d\x97\xbc\xb3\x06\x53\xa2\xd1\x6e\xb5\x18\xc3\x11\xc4\x41\x11\x65\x1b\x8a\x9f\x4b\xf4\x36\x25\xeb\xcc\xb0\x34\xfd\xf3\x0d\xad\x0f\x39\xae\x36\xed\xa4\xca\x78\x8f\xb4\xee\x4c\xa4\xb5\xd9\x33\x80\xda\x6d\x33\xbe\xe4\x7f\x31\xcc\x19\xcd\x91\xd0\x4c\xf6\xac\x62\xcc\x12\xa3\x38\xdb\x4c\x39\x5c\x8a\x70\xb7\xab\x2c\xb7\xd8\xf1\xdc\xce\xc0\xa9\xef\x83\xc4\x58\xab\x8b\xe6\x22\x8b\x35\xc3\x37\xa5\x90\x09\xfa\x99\x11\xbd\xab\x32\x0f\x65\x4b\xa5\x66\xf9\x04\x46\xc8\x7a\x1d\x31\xea\x21\xce\xcd\x5f\xd0\x96\xaf\x75\xc4\x70\x02\x89\x1f\xd8\x01\x1e\x4b\x04\xd9\x7c\xe1\xb0\x30\x5c\x5c\x21\x2d\x0c\xd7\xd2\x0f\x24\x68\xdb\x51\xf7\xf5\x76\x1e\x28\x05\xb5\xaa\xd1\x52\xa0\x60\x32\xd0\xc2\x1e\x66\xb9\xf8\x62\xf0\xb6\x5c\x5b\xb4\xf2\xb1\x18\xcc\x16\xd8\x9a\x6b\x6d\x28\x2e\xc1\x97\xd7\x42\x39\x03\x6d\x35\x63\xca\x32\x54\x96\xec\x6b\x09\x8c\x3c\x76\x8f\x61\x7c\x95\x4c\x66\x49\xf5\x9a\x64\x22\x06\x33\x1d\x3d\x71\x6b\xda\x7c\x8c\x58\x3f\x49\x48\x0e\x13\xb1\xdf\x73\x68\x95\x27\x6b\xf1\xad\xc1\xd4\x7b\x14\x1b\x6f\x1d\x8c\xbb\x64\x6f\xb7\x4d\xf8\x27\x30\x06\x1b\x6e\x23\x10\x5c\xcf\x23\x3b\x74\x95\x7e\x92\x5f\x9c\x0e\xe4\xdf\x9b\x29\x0a\x97\x28\x62\x88\xcb\x11\xad\xba\x3b\x55\x28\xcf\xa7\x51\x44\x99\x40\x3e\x18\xce\x95\xa3\x79\xfc\xfe\x3c\xee\x48\x09\xb2\x69\x5c\xd4\x49\xa0\xa8\x97\xf4\xab\x78\x63\xe7\xde\xea\xf9\xae\x13\xe2\x67\x4e\xc9\xc0\x02\xfb\x48\x89\xa1\xbc\x22\x2d\xac\xc7\x05\x0c\x51\xf1\x74\x94\x1c\xa5\x55\x25\x00\xcc\x0f\x25\xd2\xd2\xae\x20\xd0\x6d\xee\x39\x52\xac\x92\x0b\x6c\x6c\xd7\xf9\xc4\x8d\x96\x19\x6b\x5d\x1b\x26\x6f\x03\xe4\x91\xab\xc2\xfb\xd8\xfc\xb3\x80\x7c\x6d\x1a\x61\x8f\x92\x41\x3e\xff\x55\x18\xec\xc3\xe5\x1b\x15\x05\x25\xaa\xfd\xea\xa3\x05\x70\xb8\x68\x3d\xde\xa8\x26\xd9\x8d\x83\x50\xa0\x31\x65\xf8\x1b\xb2\x87\xbc\xef\xba\x94\x33\x0d\x8c\xe0\x10\x07\xb8\xb8\x39\x5c\x47\xc4\x8c\xc6\x45\x21\xe4\xc9\xf5\xfe\xae\xc8\xba\xeb\x14\xca\x24\x68\xf2\x1c\x2b\x81\x93\x04\x98\xd5\x55\x8f\x1e\x24\xe6\x3d\x8f\x33\x5d\xc1\x83\x00\x2c\x98\x91\x05\x68\xd9\x86\x19\x61\x14\xf8\x6e\x5e\x28\x48\x20\x60\x0a\xbd\x1f\x67\x02\x45\xb5\xf5\x27\xd0\xe7\x72\x9a\xa5\xc1\xed\x5c\xcd\xe8\x33\x9b\x42\xf9\x03\x3f\x4b\x38\x87\x35\x13\x0a\xb5\x7c\x3f\x48\x08\x15\xb0\x90\xd8\x73\x93\xcb\x41\xaa\x52\x26\x2e\x5b\x1d\xb7\x2a\xad\xd8\xc9\x8e\xb4\xc7\x82\x1e\x6e\xab\xc3\x69\x77\x28\xcb\x43\x82\xdf\x2a\x59\x9d\xc7\x70\xc2\x5c\xac\x91\x37\x96\xd3\x36\xd7\x88\x40\x22\x7e\x31\x8a\x39\x6a\x84\xdb\xa6\xdc\xf0\xbb\x9a\x80\xb2\x31\x24\x98\x2b\x36\xa8\x1e\xa7\x5f\xce\x82\x35\xea\x9a\xd2\x10\x46\x9d\x9a\xa4\xe5\x48\x95\x91\x21\x23\xb7\x1d\x9b\xb0\x44\xe7\x19\x16\x13\xc4\xf4\xfd\x82\x94\x59\x04\x00\xd8\x07\x3e\x8a\x10\xf1\x31\x19\x27\x37\xf6\x2a\x1e\x91\xda\xdd\x9a\x51\xa5\x33\x97\x5f\x45\xa7\x15\x7f\xec\x3c\x3c\xa5\x8b\x5e\x72\xa7\xa8\xeb\x44\x8c\x8a\xf7\x88\x5a\x6b\xb0\x5a\x80\xa3\xba\xc4\x7a\xf9\x12\xab\x14\xc9\x4a\xcf\xd8\xfc\x90\x67\x8d\xfb\xb1\x47\xc9\x32\x19\xb3\x73\x2f\x15\x59\x50\x40\xef\x5e\xb6\x7b\xd2\xbc\x8e\x08\xf8\x13\x28\x5a\xa3\x04\xbd\x84\x08\xf5\xb7\xd7\x9a\x1d\xc9\xe5\xf0\xbf\x67\x64\xcd\xa5\x3b\x97\xf5\x27\xeb\x47\xdf\xcc\x2f\x2b\x6c\x5b\xf4\x35\xc2\x76\x2e\x24\x79\x9e\x55\x15\xde\x55\xc2\x14\x96\x1a\x4a\x9e\x55\x84\x81\xe9\x5c\x96\xc5\x29\xb0\x9f\x7b\x51\x3b\x70\xe1\x10\xc5\xfa\x83\x9e\x80\xe4\xe2\xfc\xbd\x2c\x19\x65\xf5\xed\x2c\x69\xd7\x82\x73\x75\x7e\x2a\x55\x11\x43\x1e\x65\xe9\x81\xfa\x9c\xbb\xe0\x20\x60\xee\xc2\x15\xc7\x01\x14\xb3\xe2\x57\xe3\x60\x54\x22\xe7\x7f\x23\xdf\xfe\x25\x7c\x38\x46\x00\x13\x1f\x7d\x2d\x40\x1f\xc1\x80\xa3\xfa\x58\x16\xeb\xc2\xf3\x75\xc8\xda\x1c\x04\x8d\xb8\xa8\xce\x2c\x40\xd6\x48\x1b\xf5\xd2\x95\x48\x5f\x4c\xc3\xa1\xd4\xff\x23\x2d\x9a\x00\x26\x00\x41\x6f\x62\x4e\x7a\x8d\xd3\xc8\x17\x4a\xa7\xd3\x68\xb7\xf5\x44\xe2\x5b\xb6\x9c\x82\xec\x3f\x99\xab\x77\x15\x1f\x1d\xd4\xa5\x59\xaa\x93\x74\xab\x3d\x86\x05\x62\x18\xb6\x14\x87\xf0\x39\x11\xf0\xab\x0e\x47\x60\x9e\xb1\x1a\xc0\xdc\x40\x28\xc4\x01\x64\xd2\x29\x14\xb9\x2e\x08\xdc\x24\x80\x6f\x80\x17\xc0\x29\x57\xc6\x0f\x24\xe0\xea\xd7\x37\x3a\x46\x1e\x22\x22\x32\x87\xf0\x4c\xd2\x4d\x11\x3a\xf1\x37\x55\x7f\xed\xf1\x43\x32\x4f\xc1\x5a\xae\xd3\x8d\xf6\x2b\x79\x06\xe7\x15\x65\x09\xe9\x9e\x4b\xc4\x98\xba\x39\x4d\xca\x6a\xc3\xb1\x92\xe4\xe6\xe6\x00\x62\x82\xb0\x16\xf0\xcf\xa5\x49\xa7\x46\x1a\xd1\x20\xa0\x5f\xa4\x09\xa7\x27\x16\xd7\x78\xc9\xe7\xe6\xe6\x86\xdf\x05\x96\x9f\x04\x20\xf7\xcc\xef\x59\xe3\xeb\xe5\x91\x00\x03\x48\xfc\x41\x22\x18\xee\x83\xd2\xf3\x04\x48\x39\x7e\xe7\x9a\xb0\xe6\x0a\x93\x6d\xa1\xd3\xd7\x3e\xf2\x9f\x4b\xf3\x16\x8f\x0c\x23\x16\x73\x80\xc2\x48\xcc\x9f\xcb\x77\x99\xd8\xd2\x45\x48\x7c\x1a\x08\x0e\x20\xb3\xd6\x4f\x62\xd3\x4a\xf9\x3a\x0a\xa8\x8f\xac\x53\x6b\x45\x5e\xcf\xb1\xb2\xc9\xee\xc9\xd4\x1a\x25\x3b\x54\x6f\xe1\x18\xc0\x7d\x77\x21\x17\xf3\x00\xf5\x95\x56\xd3\xb2\x42\x5d\x4b\xe7\xde\x61\xd9\x06\x53\x8d\xb2\x0d\x65\xf0\x42\xf5\xce\x5a\xb0\xa3\xbe\x4c\x10\x43\xd6\x76\xca\x86\xb4\x76\x15\x38\x96\x7c\x82\xfc\x78\x77\x24\x17\xa0\x6b\xe4\xd5\xe2\xdc\x48\x2a\xdd\x3c\x07\x37\xc6\x14\xe4\x9f\x31\xb7\xc8\x7f\xaa\x80\xe2\xcd\x73\x00\x89\x0f\x6e\xe2\x78\xef\x4d\xb6\xd1\x92\x21\xf4\xa1\x04\xca\xf4\xa2\xdf\xfc\xcf\xdf\x65\xdf\x17\x37\x8a\x6d\x6e\xde\x9c\xff\x72\xe6\xe8\xe3\x51\xf2\x79\x4a\x3c\x81\x67\x28\xdf\xff\xf8\xe2\xf4\x46\x0f\xf9\xee\xf2\xa6\x05\x7e\xa2\x5f\xd0\x0c\xb1\xe7\x60\x4e\xa7\x4a\x30\xc8\x99\x43\x10\xc2\xaf\x38\x9c\x86\x92\x06\x9d\x76\x06\x8e\x12\x35\x57\x98\xcc\x54\xb1\x85\x41\xfe\xb3\x94\xcf\x5c\xbb\x33\x97\x4e\xd1\xe7\xac\x45\x7c\xb5\x3c\xb8\x81\x5f\x78\x93\xdf\xf1\xa6\xce\x4c\x6a\x24\x55\x28\x52\x93\x06\xdc\xe8\xb2\x99\x9b\xba\xdb\xd5\xde\xab\x2f\x80\x0d\x5f\x81\x4f\x40\xbf\xb0\xeb\x75\x54\xf7\x7f\x46\xcd\x7f\xb9\xa7\xa1\x2b\x8c\x71\x5c\x45\xab\xa7\x01\xf5\x28\xfa\xb7\x67\x04\x64\x82\xeb\xf7\x72\x56\x2b\x62\x1c\xe0\x5b\x24\x91\xfe\x4b\x77\xef\xbb\x08\x16\x25\x2e\xe5\x47\x7b\x59\x0c\x79\x03\x85\xfa\xae\x7c\xf0\x09\xe4\x20\x42\x2c\xc4\x9c\xc7\x25\xc6\x1c\x21\xc5\x52\x9a\x2e\xc8\x37\xf8\xe0\x82\x0a\xd4\x4a\xf0\xd3\x4a\x27\x3b\x23\x28\x39\x3e\x2e\xd2\xc0\xdc\xe8\x5d\x2e\xbe\x62\xa3\x41\xf1\x5c\x89\x50\x72\x0b\x20\x87\x8e\xb7\xe4\x0b\xc8\x8b\xbd\x5a\x5c\xd2\x58\x4d\xbc\x6d\x65\xc7\xcc\x55\xed\x4c\x82\x56\x7c\xce\xdc\x04\x8a\xfa\x60\xa8\xde\xc6\x2f\xf5\x1f\xaf\x62\x93\xfc\xe7\x8f\xd7\x5b\xe6\x88\x13\x21\x22\x09\xdd\x9e\x6d\xbe\xbe\xcd\x79\x6e\x3a\x17\xb8\xd3\x84\x6e\xbc\x9d\x5b\x3f\x60\x6d\x59\x04\xd5\x00\xb0\xdf\x07\x01\x1d\x0f\x38\x26\xb7\x83\x76\xab\x93\x7e\xd0\xc7\x1a\x2c\x48\xe9\xb7\xa5\x8e\x4c\xa8\x0a\x18\xbe\x63\x0e\xd2\xc8\xe1\xff\x86\x8e\xc1\x15\x26\xb7\xe9\xeb\xc4\xcd\x02\x0d\xab\xb5\x2b\xc7\xd6\xcc\x4b\x02\x3b\xc1\x93\x87\x9c\xa5\xa0\x56\xc4\xbf\x15\x91\x71\x86\x51\x31\xc7\xd4\x04\xdc\x1c\xaf\x2c\xc3\xd3\x54\x35\x52\x83\x7c\x8d\x54\xd3\x55\x23\x55\xcc\x5b\x94\x9f\x29\x09\xc3\xa2\x6b\x98\x6d\xb5\xec\x66\x84\xe4\x11\x58\x04\x7a\x05\x9c\xee\xa2\x23\x0a\x5d\x15\x87\x06\x20\x9c\x06\x02\x0f\x02\x4c\x9c\x27\x4d\xd3\x6a\x4b\x73\xcf\xdb\x0d\x8c\xc5\x7b\x2b\x61\x81\x37\x98\xb8\x5a\xc6\x88\x57\xb7\x51\x73\x18\x52\x1a\x20\x48\x1c\xdf\xbf\x36\xc7\x8c\x4e\xa3\x3e\x68\x20\xe2\x47\x14\x13\x51\x3c\xde\xc3\x27\xf4\xcb\x00\x06\xc1\xfd\xa7\x73\x35\xa1\x5f\xa4\xc2\x2f\x9f\x4c\x55\x8b\x7b\x4e\x45\xd0\x08\x7b\x0b\x92\xd3\x34\x0c\xa5\xa1\x20\xd5\x93\x40\x7e\x7a\x9a\x41\x6b\x4f\x05\x40\x47\x7c\xdc\x2c\x74\x5d\xde\xa0\x3c\x0f\x91\xa1\xad\x76\x9d\x8d\x33\x17\x28\xba\x7f\x30\x2c\x57\x93\x91\x3d\xcd\x4a\x46\x8e\x61\x12\x8e\x98\x18\x28\xab\xb1\xac\x4d\xb9\x5f\x59\x7c\x8e\x7d\x5f\x55\x94\x4c\xb9\xa0\xa1\x36\x46\x13\x73\xc4\xa3\xca\x3e\x11\xb1\xea\x8f\x0d\xde\x10\x71\xae\x03\x01\x40\x30\x48\x38\x16\xf9\x94\x61\xf6\x2c\x9e\x8e\x7c\x16\xcc\xa5\x30\x9f\xe4\xa7\x84\x12\xa3\x5b\x23\x2d\xa8\xf4\x48\xa1\xef\x23\xbf\x12\x54\xcc\x1c\xaf\x64\xa7\xea\x86\xe5\x4c\x62\x3e\x25\x89\xab\x4a\xec\xd3\x4c\x43\x8a\x7e\x1d\x94\x7f\x53\x39\xac\x7b\xa3\x5c\x96\x39\x33\x9f\xe6\x42\xac\x92\x94\xda\x02\x9c\xcf\x15\xbb\x6a\x6a\x83\x63\x65\xff\x97\x77\x29\x17\xf0\xf5\x30\x6f\x5a\xbb\xc3\xd9\x68\xc1\x18\x75\x76\x20\xfa\x2a\x18\xf4\x96\xdb\x82\x67\xba\x0f\x80\x31\xb3\x8e\x18\x0d\xd5\xe2\x0f\xa9\x9f\x97\x1a\xd9\xf3\xc7\xdf\x3e\xeb\xe0\xc5\x18\xa3\x84\xc4\x0f\xc5\x6a\x16\x1b\x7c\x2f\x5e\x9b\x40\x3e\x98\x20\xe8\x23\x36\x18\xe1\x40\xa0\x42\xa1\x69\xf6\x58\x6b\xfc\x4a\x35\x06\x43\xc8\xa5\xfb\xaf\x43\x0b\xba\x7e\xd0\x53\xeb\x4e\x09\x02\x1a\xee\x3d\x99\xcf\x9d\xe7\x2f\xc5\x4b\xf2\x9e\x1e\x37\x76\x76\x69\x92\x6f\xab\x16\x6c\xc9\x01\xee\xb8\xf3\x45\x31\xc5\x6f\x3f\x31\x4f\xfc\xa4\x87\x5a\xdc\x7c\x7d\xbc\xea\xa8\x3e\x28\xa2\x05\x79\x82\x5a\xbc\x50\xdf\x9f\x5d\x0b\x9c\x54\x8f\x65\x33\x17\xb0\xb6\xef\xf7\x76\xfe\x86\x8e\xcd\xac\x93\x75\x90\x00\x34\x8e\x86\x7c\xd6\xe6\x07\x82\xa0\x83\x71\xbb\x3b\x9e\xec\x8d\x7b\x86\xff\x52\x38\xe7\x62\xf4\xd9\x1f\xb2\x11\x6b\xb7\xbb\xd1\x88\xdc\x4e\xda\xa6\x69\x96\xdd\x55\x00\x1a\x9c\xcd\xbc\x26\xf4\x3c\xd1\xec\xec\x77\xd1\xa8\xeb\x1f\x36\xdb\xdd\xf6\x51\xb3\xd7\xe9\x1c\x34\x0f\x7b\xfb\xdd\xa6\x3f\xda\xdf\xf5\xba\xed\xee\x9e\xd7\xdd\x77\x40\x89\xef\x31\x00\x8d\x61\xa7\xd7\xf3\x8f\x8e\x3a\xcd\xf6\x21\x1a\x36\x7b\xbd\x83\x6e\xf3\x10\x79\x9d\x26\x1a\xb6\x77\x7b\xde\xfe\x51\x77\xb7\x33\x34\xfb\x4f\x59\xd0\x07\x8d\x11\xa5\x4d\x17\xbe\xad\x5b\xc8\x5b\xd0\x0b\x51\xcb\xa3\x61\xbf\xd7\xdb\x6d\xe4\xfc\x29\xe7\xf9\x19\x63\xfa\xed\xdb\xc3\x80\x8c\xdb\xbb\x1d\x8e\x8e\xee\x6a\x4c\x1f\xb5\xbb\x7b\xdd\xfd\x3d\xd4\x84\x87\x87\xb0\xd9\xeb\x8d\x86\xcd\xc3\xde\x5e\xbb\x89\xfc\x76\xa7\x8d\x86\xfb\x43\x6f\xcf\xab\x9a\xbe\xef\xed\xc1\xc3\xee\xd1\x61\x73\x88\xfc\x83\x66\xaf\xdb\x45\xcd\xc3\xa3\xde\x41\x73\xb4\x3f\xf2\xe1\xfe\x51\xf7\xa8\x3b\x1a\x15\xa7\x3f\x84\x2c\x9e\x7e\x37\x1c\x79\xb0\xdd\xee\x8a\xa3\xbb\x03\x3e\x6e\x71\x56\x36\xfd\xe4\xf0\x48\xde\x71\x2e\x9e\x59\x01\x0d\xb7\xd7\xee\x3c\x1f\xe4\xf2\x3d\x53\xe7\xc9\x0c\x0e\xe9\x27\x73\x14\x79\xe1\x6b\xec\xac\xa8\xc5\x7d\x3e\x84\xf6\x65\xa2\xa9\xdb\x6c\x0f\x75\x8b\xe6\xf9\xed\x98\x64\x44\x1b\x57\xd7\x97\xe7\x17\xaf\x6d\xe7\xc2\x69\x48\xa6\x3d\x7e\xbe\x7a\x77\x91\xbb\xc4\x21\xf6\xca\x0b\xf9\xcc\x4a\x0f\x21\x8e\xcf\xa8\xaf\x52\x2e\x16\xfd\xcb\x24\x9a\xa5\x9a\x28\x9b\xb3\xec\x98\x4e\xee\x5c\x9e\x0a\xc8\x0d\x92\x53\x53\x76\x59\x0e\xf4\x07\x01\x12\x02\xb1\xc1\xdd\x14\xe5\xa7\xa9\xa8\x2b\x19\x2e\xb8\xcb\x85\x8b\xaa\x7f\x46\xae\x2c\xf4\xe4\xb8\xcb\xce\xc8\x6c\x2f\x92\x40\x25\xd5\x72\xc9\xef\x69\x34\xec\xf8\x4c\x0b\x46\xb8\x45\x23\x44\xf8\x04\x8f\x84\xe4\xed\x9d\x88\xd1\x11\x0e\x90\x6b\x75\x41\x23\xf6\xd0\x9b\x56\xa3\x25\xae\x29\x2c\x9b\xb3\xec\xe0\x98\xf7\x23\x4c\xa6\xfc\xb6\x3d\x47\xa4\xaf\xd1\x69\x1b\x82\x20\xbe\xb9\x24\x77\x97\x58\x75\x70\x4c\xdf\xb1\xb7\x63\xc1\x51\x37\x3c\x81\xc6\xc9\xbb\x8b\x8b\xb3\x93\xeb\x77\x97\xcd\xb7\xaf\xdf\x5e\x37\xad\x26\xf1\xbd\x4e\xa0\x71\x35\x27\xde\x84\x51\x42\xa7\x1c\x40\x4f\x17\x87\x71\x40\xa8\xc8\x2a\x9a\x75\xf0\x1d\xf2\x39\xf1\x5e\x48\xc1\x50\xbc\xfe\x21\x77\xf1\x13\x68\x74\xf0\xc7\x73\x1c\xde\xbd\xf6\xd8\xe9\xf4\xcd\x7e\x07\x7e\xf8\x7a\xfe\x8f\xbb\x97\xd7\x77\x17\x97\x30\xa5\xd2\xb9\x0e\x66\xff\x3a\x45\x6c\x5e\x83\x52\xdd\x35\x51\xaa\xbb\x90\x50\x5d\x07\x9d\xfe\x63\x30\xc0\x2b\x75\xd8\x56\x1a\x6f\x11\x64\x1c\x59\xa9\x9c\x3e\xf8\x40\x60\xfc\xcb\xbe\x2a\x5e\xa3\x83\x35\x71\xbd\x14\x57\x77\x21\xc0\x08\x0f\x74\x4c\x33\x3e\x87\xda\x07\x05\x0c\xfa\x4b\x8c\x97\x95\x9e\x7b\x34\x98\x86\x44\xdb\x96\x72\xa4\x38\x56\x0f\xb6\xb1\xbf\xdd\x02\x57\xae\x76\x2a\xa9\x65\x8e\x26\xb5\x30\x25\xcf\xe3\x54\xb3\x17\xd0\xa9\x3f\x88\x13\x22\x2c\x79\xab\x8f\xa0\xb5\xc0\xaf\x3a\x31\xa1\x17\xb2\x0f\xb0\x0f\x5e\x80\x4e\x77\xb7\x94\x2b\x82\x8f\xa7\xaf\xa7\xf3\xe1\x39\x3b\x23\x5f\xd9\x31\x0a\x0f\xba\xbd\xf1\xdd\xed\x2d\x3e\x9d\x25\x5c\x91\xbf\x4a\xd0\xc5\x09\xbd\x76\x6f\x2d\x9c\x70\xb0\x88\x11\x0e\x1c\xfb\xa5\xce\x7d\x84\xe9\x64\x9c\x57\x07\xbb\xa6\x74\xf0\x78\x13\x3a\xb1\x7e\x18\x02\x60\xff\xc5\x76\x07\xff\xb2\xeb\x4f\x7f\xfb\x74\x3e\x9b\xed\x7d\x9a\xbd\x09\xe6\xdf\x3a\xe1\xeb\xcb\xdd\x9f\xe7\x77\x17\xdb\x4a\x34\x8c\xe8\xd4\x2c\x42\x2d\x6c\xfe\x4f\xef\x0e\xc6\xdd\xf1\xfe\x4f\xd7\xfe\x87\x5f\x3e\xc0\xee\x2d\xff\xe9\xb0\x7b\xfb\xeb\xe9\xee\x3c\xa1\x4c\xfe\x5a\x4d\xa7\x68\xec\xac\x47\x32\x76\x16\x0a\xc6\x8e\x83\x2c\xd9\x36\x9e\x21\x86\x47\x73\xf0\xf3\xc7\x6b\x7d\x59\x67\x1f\x5c\xc6\xee\x06\x80\x53\x31\xa1\x0c\x7f\x4b\x2e\x0d\xba\x45\xa4\x1e\x7d\x76\x3f\x4c\xce\x26\x5f\xc2\xdf\x5f\x46\x1f\xdf\x8f\xce\xbb\xc1\x05\xba\x8d\xfc\xde\x3f\x4e\x13\xfa\x1c\x49\x5d\x76\x42\xc9\x28\xc0\x9e\xa8\x41\xab\xdd\xfd\xb5\xd0\xca\x04\xe3\xa6\x95\xd9\xc2\x64\x21\x7d\xd4\x45\x4b\x1e\xcc\x01\x0c\x94\x6d\xa4\x4e\x64\x94\xd2\x61\xff\xf6\x53\xfb\x03\x3e\xbb\xfd\x76\xfb\xfb\xc9\xb7\x8f\xef\xd1\x79\x97\x7e\x42\x13\x7f\xf7\x2c\x26\x43\xf1\x92\x4c\xd7\xd4\x8f\xd6\x32\xf3\xa3\x45\x13\x3f\x72\xf2\x48\x76\xc7\x39\xb2\x07\x2d\x2c\x39\x3a\x7b\x33\x7b\x75\xf4\xf9\xed\xaf\x9f\xf6\x3f\x8d\x27\xa3\xb7\x47\xe3\xd7\x97\xfc\xa7\xd9\xd9\xc7\x74\xae\xb5\x85\xc5\xe3\xcd\xd8\xd4\x82\x6a\xcc\xf4\x9e\x09\x20\xad\x03\x2e\xfd\xa6\x77\x27\x6f\x9b\x67\xbf\x37\x8f\xfa\xf1\xa5\x14\x72\x0b\xe9\xab\x27\xb2\x36\xe8\xab\x68\xc6\xba\x0f\x46\xb8\xd9\xc1\x5f\xdb\xbb\x01\xf1\x83\xf0\xae\x7d\x37\xf2\x0e\x38\x16\x70\x8f\x07\x9f\x67\x87\xa6\x13\x32\x32\x7e\xcd\x5c\xd2\xa1\x33\xde\xf3\x0f\x0f\xef\xda\x01\xf3\xfc\x59\x6f\x7c\x00\x83\xe1\x01\x0f\x46\x63\xf2\x79\xd7\x9f\x0c\xf9\xe7\xbf\xfc\xbf\xbf\x9e\xfd\x7e\x7d\x79\x0c\xfe\x4b\xcf\xb8\xa5\x30\x7e\x81\x7d\x44\x84\x5c\x33\x33\x04\x80\x39\xd8\xee\xb5\x7b\xdb\xcf\x15\x2d\xd4\x9f\x27\x6f\x3e\x5c\x5d\x9f\x5d\x5e\x69\x62\xc8\x8f\x2a\x95\x9d\x2e\x2c\xc8\x00\xa9\xf6\x9d\xf1\x1e\x65\x7b\xed\x19\x9e\xb6\x0f\x28\x92\xcb\x36\x61\xb7\x5e\x77\xdf\x1f\x8f\xc4\xe7\x0e\xf4\xb6\x4d\x25\x1b\x67\x87\x55\xaf\xca\x49\x18\xf2\xf6\x6f\x15\xf2\xe4\x9a\x7f\x64\xf3\x7d\xc2\xef\x86\x5d\x7e\x11\xbe\xfa\xbc\x37\xfc\x3d\x3a\x3d\x38\x81\x8d\xad\xff\x0b\x00\x00\xff\xff\x5c\xf2\xe8\xad\xda\xd8\x00\x00") func connector_mgmtYamlBytes() ([]byte, error) { return bindataRead( @@ -93,7 +93,7 @@ func connector_mgmtYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "connector_mgmt.yaml", size: 56127, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + info := bindataFileInfo{name: "connector_mgmt.yaml", size: 55514, mode: os.FileMode(420), modTime: time.Unix(1, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/internal/connector/internal/handlers/connector_namespace.go b/internal/connector/internal/handlers/connector_namespace.go index 4f6bbf2d4..40ef812ef 100644 --- a/internal/connector/internal/handlers/connector_namespace.go +++ b/internal/connector/internal/handlers/connector_namespace.go @@ -2,15 +2,14 @@ package handlers import ( "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/dbapi" - "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/db" - "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/services/signalbus" - "net/http" - "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/public" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/presenters" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/services" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/db" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/handlers" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/services/signalbus" "github.com/goava/di" + "net/http" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/auth" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/errors" @@ -19,7 +18,7 @@ import ( ) var ( - maxConnectorNamespaceIdLength = 32 + maxConnectorNamespaceIdLength = 32 ) type ConnectorNamespaceHandler struct { @@ -39,10 +38,15 @@ func (h *ConnectorNamespaceHandler) Create(w http.ResponseWriter, r *http.Reques Validate: []handlers.Validate{ handlers.Validation("name", &resource.Name, handlers.MinLen(1)), handlers.Validation("cluster_id", &resource.ClusterId, handlers.MinLen(1), handlers.MaxLen(maxConnectorClusterIdLength)), - handlers.Validation("kind", &resource.Kind, handlers.IsOneOf(presenters.NamespaceKinds...)), }, Action: func() (interface{}, *errors.ServiceError) { + // validate tenant kind + if _, ok := presenters.AllNamespaceTenantKinds[string(resource.Kind)]; !ok { + return nil, coreservices.HandleCreateError("connector namespace", + errors.MinimumFieldLengthNotReached("%s is not valid. Must be one of: [%s, %s]", "namespace_id", + public.CONNECTORNAMESPACETENANTKIND_USER, public.CONNECTORNAMESPACETENANTKIND_ORGANISATION)) + } ctx := r.Context() claims, err := auth.GetClaimsFromContext(ctx) if err != nil { diff --git a/internal/connector/internal/handlers/connectors.go b/internal/connector/internal/handlers/connectors.go index 73af214af..a9d979628 100644 --- a/internal/connector/internal/handlers/connectors.go +++ b/internal/connector/internal/handlers/connectors.go @@ -67,7 +67,7 @@ func (h ConnectorsHandler) Create(w http.ResponseWriter, r *http.Request) { handlers.Validation("connector_type_id", &resource.ConnectorTypeId, handlers.MinLen(1), handlers.MaxLen(maxConnectorTypeIdLength)), handlers.Validation("desired_state", (*string)(&resource.DesiredState), handlers.WithDefault("ready"), handlers.IsOneOf(dbapi.ValidDesiredStates...)), validateConnectorRequest(h.connectorTypesService, &resource, tid), - handlers.Validation("deployment_location.namespace_id", &resource.DeploymentLocation.NamespaceId, + handlers.Validation("namespace_id", &resource.NamespaceId, handlers.MaxLen(maxConnectorNamespaceIdLength), validateNamespaceID(h.namespaceService, r.Context())), }, @@ -183,7 +183,7 @@ func (h ConnectorsHandler) Patch(w http.ResponseWriter, r *http.Request) { resource.Kafka = patch.Kafka resource.ServiceAccount = patch.ServiceAccount resource.SchemaRegistry = patch.SchemaRegistry - resource.DeploymentLocation = patch.DeploymentLocation + resource.NamespaceId = patch.NamespaceId // If we didn't change anything, then just skip the update... originalResource, _ := presenters.PresentConnector(dbresource) @@ -197,7 +197,7 @@ func (h ConnectorsHandler) Patch(w http.ResponseWriter, r *http.Request) { handlers.Validation("connector_type_id", &resource.ConnectorTypeId, handlers.MinLen(1), handlers.MaxLen(maxKafkaNameLength)), // handlers.Validation("kafka_id", &resource.Metadata.KafkaId, handlers.MinLen(1), handlers.MaxLen(maxKafkaNameLength)), handlers.Validation("service_account.client_id", &resource.ServiceAccount.ClientId, handlers.MinLen(1)), - handlers.Validation("deployment_location.namespace_id", &resource.DeploymentLocation.NamespaceId, handlers.MaxLen(maxConnectorNamespaceIdLength)), + handlers.Validation("namespace_id", &resource.NamespaceId, handlers.MaxLen(maxConnectorNamespaceIdLength)), } for _, v := range validates { diff --git a/internal/connector/internal/migrations/202203020000_delete_connector_target_kind.go b/internal/connector/internal/migrations/202203020000_delete_connector_target_kind.go new file mode 100644 index 000000000..8053fc43c --- /dev/null +++ b/internal/connector/internal/migrations/202203020000_delete_connector_target_kind.go @@ -0,0 +1,19 @@ +package migrations + +// Migrations should NEVER use types from other packages. Types can change +// and then migrations run on a _new_ database will fail or behave unexpectedly. +// Instead of importing types, always re-create the type in the migration, as +// is done here, even though the same type is defined in pkg/api + +import ( + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/db" + "github.com/go-gormigrate/gormigrate/v2" +) + +func deleteConnectorTargetKind(migrationId string) *gormigrate.Migration { + + return db.CreateMigrationFromActions(migrationId, + db.ExecAction(`ALTER TABLE connectors DROP COLUMN target_kind`, + `ALTER TABLE connectors ADD COLUMN target_kind text`), + ) +} diff --git a/internal/connector/internal/migrations/migrations.go b/internal/connector/internal/migrations/migrations.go index 023778d9d..bb080f94b 100644 --- a/internal/connector/internal/migrations/migrations.go +++ b/internal/connector/internal/migrations/migrations.go @@ -31,6 +31,7 @@ var migrations = []*gormigrate.Migration{ addConnectorNamespaceTables("202202070000"), addConnectorNamespaceDeployment("202202220000"), addDeletedAtIndex("202202280000"), + deleteConnectorTargetKind("202203020000"), } func New(dbConfig *db.DatabaseConfig) (*db.Migration, func(), error) { diff --git a/internal/connector/internal/presenters/connector.go b/internal/connector/internal/presenters/connector.go index cdec62662..f772f2442 100644 --- a/internal/connector/internal/presenters/connector.go +++ b/internal/connector/internal/presenters/connector.go @@ -17,14 +17,13 @@ func ConvertConnector(from public.Connector) (*dbapi.Connector, *errors.ServiceE } var namespaceId *string - if from.DeploymentLocation.NamespaceId != "" { - namespaceId = &from.DeploymentLocation.NamespaceId + if from.NamespaceId != "" { + namespaceId = &from.NamespaceId } return &dbapi.Connector{ Meta: api.Meta{ ID: from.Id, }, - TargetKind: dbapi.AddonTargetKind, NamespaceId: namespaceId, Name: from.Name, Owner: from.Owner, @@ -74,10 +73,7 @@ func PresentConnector(from *dbapi.Connector) (public.Connector, *errors.ServiceE CreatedAt: from.CreatedAt, ModifiedAt: from.UpdatedAt, ResourceVersion: from.Version, - - DeploymentLocation: public.DeploymentLocation{ - NamespaceId: namespaceId, - }, + NamespaceId: namespaceId, ConnectorTypeId: from.ConnectorTypeId, Connector: spec, Status: public.ConnectorStatusStatus{ diff --git a/internal/connector/internal/presenters/connector_namespace.go b/internal/connector/internal/presenters/connector_namespace.go index 342c1e8cc..454381750 100644 --- a/internal/connector/internal/presenters/connector_namespace.go +++ b/internal/connector/internal/presenters/connector_namespace.go @@ -1,23 +1,21 @@ package presenters import ( - "encoding/json" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/admin/private" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/dbapi" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/public" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/api" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/db" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/errors" + "k8s.io/apimachinery/pkg/util/json" "strings" "time" ) -const ( - UserKind string = "user" - OrganisationKind string = "organisation" -) - -var NamespaceKinds = []string{UserKind, OrganisationKind} +var AllNamespaceTenantKinds = map[string]public.ConnectorNamespaceTenantKind{ + string(public.CONNECTORNAMESPACETENANTKIND_USER): public.CONNECTORNAMESPACETENANTKIND_USER, + string(public.CONNECTORNAMESPACETENANTKIND_ORGANISATION): public.CONNECTORNAMESPACETENANTKIND_ORGANISATION, +} func ConvertConnectorNamespaceRequest(namespaceRequest *public.ConnectorNamespaceRequest, userID string, organisationID string) (*dbapi.ConnectorNamespace, *errors.ServiceError) { @@ -36,14 +34,14 @@ func ConvertConnectorNamespaceRequest(namespaceRequest *public.ConnectorNamespac result.Annotations[i].Value = annotation.Value } switch namespaceRequest.Kind { - case UserKind: + case public.CONNECTORNAMESPACETENANTKIND_USER: result.TenantUserId = &userID result.TenantUser = &dbapi.ConnectorTenantUser{ Model: db.Model{ ID: userID, }, } - case OrganisationKind: + case public.CONNECTORNAMESPACETENANTKIND_ORGANISATION: if organisationID == "" { return nil, errors.BadRequest("missing organization for tenant organisation namespace") } @@ -65,8 +63,8 @@ func ConvertConnectorNamespaceEvalRequest(namespaceRequest *public.ConnectorName Model: db.Model{ ID: api.NewID(), }, - Name: namespaceRequest.Name, - Owner: userID, + Name: namespaceRequest.Name, + Owner: userID, } result.Annotations = make([]dbapi.ConnectorNamespaceAnnotation, len(namespaceRequest.Annotations)) for i, annotation := range namespaceRequest.Annotations { @@ -93,15 +91,15 @@ func ConvertConnectorNamespaceWithTenantRequest(namespaceRequest *private.Connec ClusterId: namespaceRequest.ClusterId, } switch namespaceRequest.Tenant.Kind { - case UserKind: - result.TenantUserId = &namespaceRequest.Tenant.UserId + case private.USER: + result.TenantUserId = &namespaceRequest.Tenant.Id result.TenantUser = &dbapi.ConnectorTenantUser{ Model: db.Model{ ID: *result.TenantUserId, }, } - case OrganisationKind: - result.TenantOrganisationId = &namespaceRequest.Tenant.OrganisationId + case private.ORGANISATION: + result.TenantOrganisationId = &namespaceRequest.Tenant.Id result.TenantOrganisation = &dbapi.ConnectorTenantOrganisation{ Model: db.Model{ ID: *result.TenantOrganisationId, @@ -144,12 +142,12 @@ func PresentConnectorNamespace(namespace *dbapi.ConnectorNamespace) public.Conne Annotations: annotations, } if namespace.TenantUser != nil { - result.Tenant.Kind = UserKind - result.Tenant.UserId = namespace.TenantUser.ID + result.Tenant.Kind = public.CONNECTORNAMESPACETENANTKIND_USER + result.Tenant.Id = namespace.TenantUser.ID } if namespace.TenantOrganisation != nil { - result.Tenant.Kind = OrganisationKind - result.Tenant.OrganisationId = namespace.TenantOrganisation.ID + result.Tenant.Kind = public.CONNECTORNAMESPACETENANTKIND_ORGANISATION + result.Tenant.Id = namespace.TenantOrganisation.ID } if namespace.Expiration != nil { result.Expiration = getTimestamp(*namespace.Expiration) @@ -182,12 +180,12 @@ func PresentPrivateConnectorNamespace(namespace *dbapi.ConnectorNamespace) priva Annotations: annotations, } if namespace.TenantUser != nil { - result.Tenant.Kind = UserKind - result.Tenant.UserId = namespace.TenantUser.ID + result.Tenant.Kind = private.USER + result.Tenant.Id = namespace.TenantUser.ID } if namespace.TenantOrganisationId != nil { - result.Tenant.Kind = OrganisationKind - result.Tenant.OrganisationId = namespace.TenantOrganisation.ID + result.Tenant.Kind = private.ORGANISATION + result.Tenant.Id = namespace.TenantOrganisation.ID } if namespace.Expiration != nil { result.Expiration = getTimestamp(*namespace.Expiration) diff --git a/internal/connector/internal/presenters/connector_request.go b/internal/connector/internal/presenters/connector_request.go index 51b27c147..dbd1b8526 100644 --- a/internal/connector/internal/presenters/connector_request.go +++ b/internal/connector/internal/presenters/connector_request.go @@ -15,12 +15,11 @@ func ConvertConnectorRequest(from public.ConnectorRequest) (*dbapi.Connector, *e return nil, errors.BadRequest("invalid connector spec: %v", err) } - namespaceId := &from.DeploymentLocation.NamespaceId + namespaceId := &from.NamespaceId if *namespaceId == "" { namespaceId = nil } return &dbapi.Connector{ - TargetKind: dbapi.AddonTargetKind, NamespaceId: namespaceId, Name: from.Name, ConnectorTypeId: from.ConnectorTypeId, diff --git a/internal/connector/internal/services/connector_namespaces.go b/internal/connector/internal/services/connector_namespaces.go index 054f132db..08d936fb5 100644 --- a/internal/connector/internal/services/connector_namespaces.go +++ b/internal/connector/internal/services/connector_namespaces.go @@ -256,9 +256,9 @@ func (k *connectorNamespaceService) CreateDefaultNamespace(ctx context.Context, owner := connectorCluster.Owner organisationId := connectorCluster.OrganisationId - kind := presenters.UserKind + kind := public.CONNECTORNAMESPACETENANTKIND_USER if organisationId != "" { - kind = presenters.OrganisationKind + kind = public.CONNECTORNAMESPACETENANTKIND_ORGANISATION } namespaceRequest, err := presenters.ConvertConnectorNamespaceRequest(&public.ConnectorNamespaceRequest{ diff --git a/internal/connector/internal/workers/connector_mgr.go b/internal/connector/internal/workers/connector_mgr.go index 7dbe53cbb..5bd636276 100644 --- a/internal/connector/internal/workers/connector_mgr.go +++ b/internal/connector/internal/workers/connector_mgr.go @@ -201,50 +201,43 @@ func (k *ConnectorManager) ReconcileConnectorCatalogEntry(id string, channel str //goland:noinspection VacuumSwitchStatement func (k *ConnectorManager) reconcileAssigning(ctx context.Context, connector *dbapi.Connector) error { - switch connector.TargetKind { - case dbapi.AddonTargetKind: - - var namespace *dbapi.ConnectorNamespace - namespace, err := k.connectorClusterService.FindReadyNamespace(connector.Owner, connector.OrganisationId, connector.NamespaceId) - if err != nil { - return errors.Wrapf(err, "failed to find namespace for connector request %s", connector.ID) - } - if namespace == nil { - // we will try to find a ready cluster again in the next reconcile - return nil - } - - channelVersion, err := k.connectorTypesService.GetLatestConnectorShardMetadataID(connector.ConnectorTypeId, connector.Channel) - if err != nil { - return errors.Wrapf(err, "failed to get latest channel version for connector request %s", connector.ID) - } + var namespace *dbapi.ConnectorNamespace + namespace, err := k.connectorClusterService.FindReadyNamespace(connector.Owner, connector.OrganisationId, connector.NamespaceId) + if err != nil { + return errors.Wrapf(err, "failed to find namespace for connector request %s", connector.ID) + } + if namespace == nil { + // we will try to find a ready cluster again in the next reconcile + return nil + } - var status = dbapi.ConnectorStatus{} - status.ID = connector.ID - status.NamespaceID = &namespace.ID - status.Phase = dbapi.ConnectorStatusPhaseAssigned - if err = k.connectorService.SaveStatus(ctx, status); err != nil { - return errors.Wrapf(err, "failed to update connector status %s with namespace details", status.ID) - } + channelVersion, err := k.connectorTypesService.GetLatestConnectorShardMetadataID(connector.ConnectorTypeId, connector.Channel) + if err != nil { + return errors.Wrapf(err, "failed to get latest channel version for connector request %s", connector.ID) + } - deployment := dbapi.ConnectorDeployment{ - Meta: api.Meta{ - ID: api.NewID(), - }, - ConnectorID: connector.ID, - ClusterID: namespace.ClusterId, - NamespaceID: namespace.ID, - ConnectorVersion: connector.Version, - ConnectorTypeChannelId: channelVersion, - Status: dbapi.ConnectorDeploymentStatus{}, - } + var status = dbapi.ConnectorStatus{} + status.ID = connector.ID + status.NamespaceID = &namespace.ID + status.Phase = dbapi.ConnectorStatusPhaseAssigned + if err = k.connectorService.SaveStatus(ctx, status); err != nil { + return errors.Wrapf(err, "failed to update connector status %s with namespace details", status.ID) + } - if err = k.connectorClusterService.SaveDeployment(ctx, &deployment); err != nil { - return errors.Wrapf(err, "failed to create connector deployment for connector %s", connector.ID) - } + deployment := dbapi.ConnectorDeployment{ + Meta: api.Meta{ + ID: api.NewID(), + }, + ConnectorID: connector.ID, + ClusterID: namespace.ClusterId, + NamespaceID: namespace.ID, + ConnectorVersion: connector.Version, + ConnectorTypeChannelId: channelVersion, + Status: dbapi.ConnectorDeploymentStatus{}, + } - default: - return errors.Errorf("target kind not supported: %s", connector.TargetKind) + if err = k.connectorClusterService.SaveDeployment(ctx, &deployment); err != nil { + return errors.Wrapf(err, "failed to create connector deployment for connector %s", connector.ID) } return nil } diff --git a/internal/connector/test/integration/features/connector-agent-api.feature b/internal/connector/test/integration/features/connector-agent-api.feature index 5a673db5d..e516dc564 100644 --- a/internal/connector/test/integration/features/connector-agent-api.feature +++ b/internal/connector/test/integration/features/connector-agent-api.feature @@ -42,9 +42,7 @@ Feature: connector agent API { "kind": "Connector", "name": "example 1", - "deployment_location": { - "namespace_id": "${connector_namespace_id}" - }, + "namespace_id": "${connector_namespace_id}", "channel":"stable", "connector_type_id": "aws-sqs-source-v1alpha1", "kafka": { @@ -485,9 +483,7 @@ Feature: connector agent API "kafka_topic": "test" }, "connector_type_id": "aws-sqs-source-v1alpha1", - "deployment_location": { - "namespace_id": "${connector_namespace_id}" - }, + "namespace_id": "${connector_namespace_id}", "href": "/api/connector_mgmt/v1/kafka_connectors/${connector_id}", "id": "${connector_id}", "kafka": { @@ -883,9 +879,9 @@ Feature: connector agent API When I GET path "/v1/kafka_connectors/${connector_id}" Then the response code should be 200 And the ".status.state" selection from the response should match "assigning" - And the ".deployment_location" selection from the response should match json: + And the ".namespace_id" selection from the response should match json: """ - {} + null """ @@ -944,9 +940,7 @@ Feature: connector agent API { "kind": "Connector", "name": "example 1", - "deployment_location": { - "namespace_id": "${connector_namespace_id}" - }, + "namespace_id": "${connector_namespace_id}", "channel":"stable", "connector_type_id": "log_sink_0.1", "kafka": { diff --git a/internal/connector/test/integration/features/connector-api.feature b/internal/connector/test/integration/features/connector-api.feature index dc59d1713..04af88838 100644 --- a/internal/connector/test/integration/features/connector-api.feature +++ b/internal/connector/test/integration/features/connector-api.feature @@ -1102,8 +1102,6 @@ Feature: create a connector { "kind": "Connector", "name": "example 1", - "deployment_location": { - }, "kafka": { "id":"mykafka", "url": "kafka.hostname" @@ -1141,9 +1139,7 @@ Feature: create a connector { "kind": "Connector", "name": "example 1", - "deployment_location": { - "namespace_id": "default" - }, + "namespace_id": "default", "connector_type_id": "aws-sqs-source-v1alpha1", "kafka": { "id":"mykafka", @@ -1171,7 +1167,7 @@ Feature: create a connector "id": "21", "kind": "Error", "operation_id": "${response.operation_id}", - "reason": "deployment_location.namespace_id is not valid: KAFKAS-MGMT-9: failed to get connector namespace: record not found" + "reason": "namespace_id is not valid: KAFKAS-MGMT-9: failed to get connector namespace: record not found" } """ @@ -1183,8 +1179,6 @@ Feature: create a connector { "kind": "Connector", "name": "example 1", - "deployment_location": { - }, "connector_type_id": "aws-sqs-source-v1alpha1", "kafka": { "id":"mykafka", @@ -1239,8 +1233,6 @@ Feature: create a connector "kafka_topic": "test" }, "connector_type_id": "aws-sqs-source-v1alpha1", - "deployment_location": { - }, "href": "/api/connector_mgmt/v1/kafka_connectors/${connector_id}", "id": "${connector_id}", "kind": "Connector", @@ -1289,8 +1281,6 @@ Feature: create a connector "client_secret": "", "client_id": "myclient" }, - "deployment_location": { - }, "connector_type_id": "aws-sqs-source-v1alpha1", "channel": "stable", "connector": { @@ -1791,8 +1781,6 @@ Feature: create a connector { "kind": "Connector", "name": "example 1", - "deployment_location": { - }, "connector_type_id": "aws-sqs-source-v1alpha1", "kafka": { "id":"mykafka", @@ -1839,8 +1827,6 @@ Feature: create a connector }, "connector": {}, "connector_type_id": "foo", - "deployment_location": { - }, "href": "/api/connector_mgmt/v1/kafka_connectors/${connector_id}", "id": "${connector_id}", "kind": "Connector", @@ -1887,8 +1873,6 @@ Feature: create a connector "client_secret": "", "client_id": "myclient" }, - "deployment_location": { - }, "connector": {}, "connector_type_id": "foo", "channel": "stable", diff --git a/internal/connector/test/integration/features/connector-multitenancy-api.feature b/internal/connector/test/integration/features/connector-multitenancy-api.feature index a4dfb466f..bf69fd6ca 100644 --- a/internal/connector/test/integration/features/connector-multitenancy-api.feature +++ b/internal/connector/test/integration/features/connector-multitenancy-api.feature @@ -105,7 +105,7 @@ Feature: connector namespaces API ], "tenant": { "kind": "user", - "user_id": "${}" + "id": "${}" } } """ @@ -176,7 +176,7 @@ Feature: connector namespaces API "owner": "${dusty_user_id}", "tenant": { "kind": "organisation", - "organisation_id": "13640230" + "id": "13640230" }, "annotations": [ { @@ -231,7 +231,7 @@ Feature: connector namespaces API ], "tenant": { "kind": "organisation", - "organisation_id": "13640230" + "id": "13640230" } } """ @@ -256,7 +256,7 @@ Feature: connector namespaces API "owner": "${dusty_user_id}", "tenant": { "kind": "organisation", - "organisation_id": "13640230" + "id": "13640230" }, "annotations": [ { @@ -284,7 +284,7 @@ Feature: connector namespaces API ], "tenant": { "kind": "organisation", - "organisation_id": "13640230" + "id": "13640230" } } ], @@ -316,7 +316,7 @@ Feature: connector namespaces API "owner": "${dusty_user_id}", "tenant": { "kind": "organisation", - "organisation_id": "13640230" + "id": "13640230" }, "annotations": [ { @@ -384,7 +384,7 @@ Feature: connector namespaces API "owner": "${}", "tenant": { "kind": "organisation", - "organisation_id": "${response.items[0].tenant.organisation_id}" + "id": "${response.items[0].tenant.id}" }, "annotations": [ { @@ -417,7 +417,7 @@ Feature: connector namespaces API ], "tenant": { "kind": "user", - "user_id": "${}" + "id": "${}" } } """ @@ -442,7 +442,7 @@ Feature: connector namespaces API ], "tenant": { "kind": "user", - "user_id": "${}" + "id": "${}" } } """ @@ -469,7 +469,7 @@ Feature: connector namespaces API "owner": "${}", "tenant": { "kind": "organisation", - "organisation_id": "${response.items[0].tenant.organisation_id}" + "id": "${response.items[0].tenant.id}" }, "annotations": [ { @@ -543,7 +543,7 @@ Feature: connector namespaces API "owner": "${guapo_user_id}", "tenant": { "kind": "organisation", - "organisation_id": "13640231" + "id": "13640231" }, "annotations": [ { @@ -576,7 +576,7 @@ Feature: connector namespaces API ], "tenant": { "kind": "organisation", - "organisation_id": "13640231" + "id": "13640231" } } """ @@ -601,7 +601,7 @@ Feature: connector namespaces API ], "tenant": { "kind": "organisation", - "organisation_id": "13640231" + "id": "13640231" } } """ @@ -628,7 +628,7 @@ Feature: connector namespaces API "owner": "${guapo_user_id}", "tenant": { "kind": "organisation", - "organisation_id": "13640231" + "id": "13640231" }, "annotations": [ { diff --git a/internal/connector/test/integration/features/connector-old-path.feature b/internal/connector/test/integration/features/connector-old-path.feature index d7127c333..56e462ea8 100644 --- a/internal/connector/test/integration/features/connector-old-path.feature +++ b/internal/connector/test/integration/features/connector-old-path.feature @@ -21,8 +21,6 @@ Feature: the old connectors path are still valid "name": "example 1", "kafka_id":"mykafka" }, - "deployment_location": { - }, "kafka": { "bootstrap_server": "kafka.hostname", "client_id": "myclient", diff --git a/openapi/connector_mgmt.yaml b/openapi/connector_mgmt.yaml index 4c1e3f511..3aa4fa17e 100644 --- a/openapi/connector_mgmt.yaml +++ b/openapi/connector_mgmt.yaml @@ -1137,12 +1137,6 @@ components: client_secret: type: string - DeploymentLocation: - type: object - properties: - namespace_id: - type: string - VersionMetadata: allOf: - $ref: "#/components/schemas/ObjectReference" @@ -1305,17 +1299,16 @@ components: required: - name - connector_type_id - - deployment_location - desired_state properties: name: type: string connector_type_id: type: string + namespace_id: + type: string channel: $ref: "#/components/schemas/Channel" - deployment_location: - $ref: "#/components/schemas/DeploymentLocation" desired_state: $ref: "#/components/schemas/ConnectorDesiredState" @@ -1456,33 +1449,22 @@ components: - $ref: "#/components/schemas/ObjectMeta" - $ref: "#/components/schemas/ConnectorNamespaceRequestMeta" + ConnectorNamespaceTenantKind: + type: string + enum: + - user + - organisation + ConnectorNamespaceTenant: type: object required: - kind - discriminator: - propertyName: kind - mapping: - user: "#/components/schemas/ConnectorNamespaceTenantUser" - organisation: "#/components/schemas/ConnectorNamespaceTenantOrganisation" - oneOf: - - $ref: "#/components/schemas/ConnectorNamespaceTenantUser" - - $ref: "#/components/schemas/ConnectorNamespaceTenantOrganisation" - - ConnectorNamespaceTenantUser: - type: object - properties: - kind: - type: string - user_id: - type: string - - ConnectorNamespaceTenantOrganisation: - type: object + - id properties: kind: - type: string - organisation_id: + $ref: "#/components/schemas/ConnectorNamespaceTenantKind" + id: + description: Either user or organisation id depending on the value of kind type: string ConnectorNamespaceRequest: @@ -1498,8 +1480,7 @@ components: cluster_id: type: string kind: - type: string - description: One of 'user' or 'organisation' similar to 'ConnectorNamespaceTenant' + $ref: "#/components/schemas/ConnectorNamespaceTenantKind" ConnectorNamespaceEvalRequest: description: An evaluation connector namespace create request @@ -1746,8 +1727,7 @@ components: ConnectorCreateExample: value: name: MyLogger - deployment_location: - namespace_id: "9bsv0s7tne7g02gh5g4g" + namespace_id: "9bsv0s7tne7g02gh5g4g" kafka: id: "9bsv0s6brfr002pfnkh0" client_id: "srvc-acct-162ef2d8-0209-4117-8462-df63c2025c26" diff --git a/pkg/handlers/validation_builder.go b/pkg/handlers/validation_builder.go index 17e9a062c..17dc8a90e 100644 --- a/pkg/handlers/validation_builder.go +++ b/pkg/handlers/validation_builder.go @@ -54,6 +54,6 @@ func IsOneOf(options ...string) ValidateOption { } } } - return errors.MinimumFieldLengthNotReached("%s is not valid. Must be one of: %s", field, strings.Join(options, ", ")) + return errors.BadRequest("%s is not valid. Must be one of: %s", field, strings.Join(options, ", ")) } } From d4b0113ecfa9aa462a4193cc32e08bc1f309f596 Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Wed, 2 Mar 2022 21:51:56 -0800 Subject: [PATCH 08/12] feat(connector): added namespace worker to reap expired namespaces --- go.sum | 30 --------- .../internal/handlers/connector_admin.go | 2 +- .../internal/handlers/connector_namespace.go | 33 ++++------ ...203030000_add_connector_namespace_lease.go | 45 +++++++++++++ .../internal/migrations/migrations.go | 1 + .../internal/services/connector_namespaces.go | 26 +++++--- .../internal/workers/namespace_mgr.go | 66 +++++++++++++++++++ internal/connector/providers.go | 1 + .../test/integration/feature_test.go | 2 +- .../connector-multitenancy-api.feature | 45 ++++++++++--- 10 files changed, 179 insertions(+), 72 deletions(-) create mode 100644 internal/connector/internal/migrations/202203030000_add_connector_namespace_lease.go create mode 100644 internal/connector/internal/workers/namespace_mgr.go diff --git a/go.sum b/go.sum index 0974c3f30..4e2ec754c 100644 --- a/go.sum +++ b/go.sum @@ -145,11 +145,6 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chirino/graphql v0.0.0-20210706215952-25574d349bd0/go.mod h1:QJryzmxY+8v+nP7+HjPsMxT4q+tfZZq387ZdG0md+kU= -github.com/chirino/graphql v0.0.0-20210707003802-dfaf250c773e h1:6nzwfRcXUlHwVelYS03FAjhGn1IRPrdBJ1ORzmgmk7A= -github.com/chirino/graphql v0.0.0-20210707003802-dfaf250c773e/go.mod h1:QJryzmxY+8v+nP7+HjPsMxT4q+tfZZq387ZdG0md+kU= -github.com/chirino/graphql-4-apis v0.0.0-20210708183827-e0ef9ee72fe3 h1:QYPhFi3XltRt7aTURUNCYcF5/C2C58eq4AJg8Kx6XDg= -github.com/chirino/graphql-4-apis v0.0.0-20210708183827-e0ef9ee72fe3/go.mod h1:Yk+Nac2yqgOYUxEN3FPNvWdcQBdvhEBvBLk+5vWqVVk= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -161,7 +156,6 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -248,13 +242,10 @@ github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nI github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/friendsofgo/graphiql v0.2.2/go.mod h1:8Y2kZ36AoTGWs78+VRpvATyt3LJBx0SZXmay80ZTRWo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= -github.com/getkin/kin-openapi v0.66.0 h1:GNEnjUYK5j5TR8CrXBQ5Nao9kVd7kPpoTr0Y0q4eaSA= -github.com/getkin/kin-openapi v0.66.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getsentry/sentry-go v0.3.1 h1:yTTEl5wqb8mECvTujvyqSi/WjKQnUQL9F921EIDuVqs= github.com/getsentry/sentry-go v0.3.1/go.mod h1:rnN2T5/zgkxv1oOpRVzfRqvjCw7XM6cjbDGmGyY/+8E= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -299,8 +290,6 @@ github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwds github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= @@ -328,8 +317,6 @@ github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/ github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= @@ -460,7 +447,6 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/graphql-go/graphql v0.7.8/go.mod h1:k6yrAYQaSP59DC5UVxbgxESlmVyojThKdORUqGDGmrI= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -610,8 +596,6 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -685,9 +669,6 @@ github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -795,8 +776,6 @@ github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= @@ -879,7 +858,6 @@ github.com/santhosh-tekuri/jsonschema/v3 v3.0.1 h1:tQVL4vmtH0NYlua++DZCaCUCIs8Jd github.com/santhosh-tekuri/jsonschema/v3 v3.0.1/go.mod h1:oOUSf2vgwmcYO4CkIJnEKle02MmEeI3cyItX+fxgpzg= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/segmentio/ksuid v1.0.2/go.mod h1:BXuJDr2byAiHuQaQtSKoXh1J0YmUDurywOXgB2w+OSU= github.com/segmentio/ksuid v1.0.3 h1:FoResxvleQwYiPAVKe1tMUlEirodZqlqglIuFsdDntY= github.com/segmentio/ksuid v1.0.3/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= github.com/selvatico/go-mocket v1.0.7 h1:jbVa7RkoOCzBanQYiYF+VWgySHZogg25fOIKkM38q5k= @@ -889,9 +867,7 @@ github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9Nz github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -946,11 +922,6 @@ github.com/tidwall/pretty v0.0.0-20190325153808-1166b9ac2b65/go.mod h1:XNkn88O1C github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= -github.com/uber/jaeger-client-go v2.14.1-0.20180928181052-40fb3b2c4120+incompatible h1:Dw0AFQs6RGO8RxMPGP2LknN/VtHolVH82P9PP0Ni+9w= -github.com/uber/jaeger-client-go v2.14.1-0.20180928181052-40fb3b2c4120+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-lib v1.5.0 h1:OHbgr8l656Ub3Fw5k9SWnBfIEwvoHQ+W2y+Aa9D1Uyo= -github.com/uber/jaeger-lib v1.5.0/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -1310,7 +1281,6 @@ golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200128220307-520188d60f50/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= diff --git a/internal/connector/internal/handlers/connector_admin.go b/internal/connector/internal/handlers/connector_admin.go index bda954ace..7daddb282 100644 --- a/internal/connector/internal/handlers/connector_admin.go +++ b/internal/connector/internal/handlers/connector_admin.go @@ -298,7 +298,7 @@ func (h *ConnectorAdminHandler) DeleteConnectorNamespace(writer http.ResponseWri return nil, serviceError } - serviceError = h.NamespaceService.Delete(request.Context(), namespaceId) + serviceError = h.NamespaceService.Delete(namespaceId) return nil, serviceError }, } diff --git a/internal/connector/internal/handlers/connector_namespace.go b/internal/connector/internal/handlers/connector_namespace.go index 40ef812ef..8024e8832 100644 --- a/internal/connector/internal/handlers/connector_namespace.go +++ b/internal/connector/internal/handlers/connector_namespace.go @@ -1,6 +1,7 @@ package handlers import ( + "fmt" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/dbapi" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/api/public" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/presenters" @@ -59,7 +60,7 @@ func (h *ConnectorNamespaceHandler) Create(w http.ResponseWriter, r *http.Reques if serr != nil { return nil, serr } - if err := h.checkAuthorizedAccess(userID, convResource, organisationId); err != nil { + if err := h.checkAuthorizedClusterAccess(userID, convResource, organisationId); err != nil { return nil, err } @@ -91,7 +92,7 @@ func (h *ConnectorNamespaceHandler) Create(w http.ResponseWriter, r *http.Reques handlers.Handle(w, r, cfg, http.StatusCreated) } -func (h *ConnectorNamespaceHandler) checkAuthorizedAccess(userID string, convResource *dbapi.ConnectorNamespace, +func (h *ConnectorNamespaceHandler) checkAuthorizedClusterAccess(userID string, convResource *dbapi.ConnectorNamespace, organisationId string) *errors.ServiceError { var ownerClusterIds []string @@ -208,7 +209,7 @@ func (h *ConnectorNamespaceHandler) Delete(w http.ResponseWriter, r *http.Reques handlers.MinLen(1), handlers.MaxLen(maxConnectorNamespaceIdLength)), }, Action: func() (i interface{}, serviceError *errors.ServiceError) { - err := h.Service.Delete(r.Context(), connectorNamespaceId) + err := h.Service.Delete(connectorNamespaceId) return nil, err }, } @@ -227,29 +228,17 @@ func (h *ConnectorNamespaceHandler) List(w http.ResponseWriter, r *http.Request) return nil, errors.Unauthenticated("user not authenticated") } userId := auth.GetUsernameFromClaims(claims) - var ownerClusterIds []string - if err := h.Service.GetOwnerClusterIds(userId, &ownerClusterIds); err != nil { - return nil, err - } organisationId := auth.GetOrgIdFromClaims(claims) - var orgClusterIds []string - if err := h.Service.GetOrgClusterIds(userId, organisationId, &orgClusterIds); err != nil { - return nil, err - } - clusterIds := append(ownerClusterIds, orgClusterIds...) - if len(clusterIds) == 0 { - // user doesn't have access to any clusters - return public.ConnectorNamespaceList{ - Kind: "ConnectorNamespaceList", - Page: 1, - Size: 0, - Total: 0, - Items: make([]public.ConnectorNamespace, 0), - }, nil + userQuery := fmt.Sprintf("owner = '%s' or tenant_user_id = '%s' or tenant_organisation_id = '%s'", + userId, userId, organisationId) + if len(listArgs.Search) == 0 { + listArgs.Search = userQuery + } else { + listArgs.Search = fmt.Sprintf("%s AND %s", listArgs.Search, userQuery) } - resources, paging, serviceError := h.Service.List(ctx, clusterIds, listArgs) + resources, paging, serviceError := h.Service.List(ctx, nil, listArgs) if serviceError != nil { return nil, serviceError } diff --git a/internal/connector/internal/migrations/202203030000_add_connector_namespace_lease.go b/internal/connector/internal/migrations/202203030000_add_connector_namespace_lease.go new file mode 100644 index 000000000..645257e02 --- /dev/null +++ b/internal/connector/internal/migrations/202203030000_add_connector_namespace_lease.go @@ -0,0 +1,45 @@ +package migrations + +// Migrations should NEVER use types from other packages. Types can change +// and then migrations run on a _new_ database will fail or behave unexpectedly. +// Instead of importing types, always re-create the type in the migration, as +// is done here, even though the same type is defined in pkg/api + +import ( + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/api" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/db" + "github.com/go-gormigrate/gormigrate/v2" + "gorm.io/gorm" + "time" +) + +func addConnectorNamespaceLease(migrationId string) *gormigrate.Migration { + + type LeaderLease struct { + db.Model + Leader string + LeaseType string + Expires *time.Time + } + + // add missing deleted_at index caused by incorrectly using api.Meta instead of db.Model in dbapi.* structs + return db.CreateMigrationFromActions(migrationId, + db.FuncAction(func(tx *gorm.DB) error { + // We don't want to delete the leader lease table on rollback because it's shared with the kas-fleet-manager + // so we just create it here if it does not exist yet.. but we don't drop it on rollback. + err := tx.Migrator().AutoMigrate(&LeaderLease{}) + if err != nil { + return err + } + now := time.Now().Add(-time.Minute) //set to a expired time + return tx.Create(&api.LeaderLease{ + Expires: &now, + LeaseType: "connector_namespace", + }).Error + }, func(tx *gorm.DB) error { + // The leader lease table may have already been dropped, by the kafka migration rollback, ignore error + _ = tx.Where("lease_type = ?", "connector_namespace").Delete(&api.LeaderLease{}) + return nil + }), + ) +} diff --git a/internal/connector/internal/migrations/migrations.go b/internal/connector/internal/migrations/migrations.go index bb080f94b..9fdae8748 100644 --- a/internal/connector/internal/migrations/migrations.go +++ b/internal/connector/internal/migrations/migrations.go @@ -32,6 +32,7 @@ var migrations = []*gormigrate.Migration{ addConnectorNamespaceDeployment("202202220000"), addDeletedAtIndex("202202280000"), deleteConnectorTargetKind("202203020000"), + addConnectorNamespaceLease("202203030000"), } func New(dbConfig *db.DatabaseConfig) (*db.Migration, func(), error) { diff --git a/internal/connector/internal/services/connector_namespaces.go b/internal/connector/internal/services/connector_namespaces.go index 08d936fb5..aa8eac865 100644 --- a/internal/connector/internal/services/connector_namespaces.go +++ b/internal/connector/internal/services/connector_namespaces.go @@ -21,11 +21,12 @@ type ConnectorNamespaceService interface { Update(ctx context.Context, request *dbapi.ConnectorNamespace) *errors.ServiceError Get(ctx context.Context, namespaceID string) (*dbapi.ConnectorNamespace, *errors.ServiceError) List(ctx context.Context, clusterIDs []string, listArguments *services.ListArguments) (dbapi.ConnectorNamespaceList, *api.PagingMeta, *errors.ServiceError) - Delete(ctx context.Context, namespaceName string) *errors.ServiceError + Delete(namespaceId string) *errors.ServiceError SetEvalClusterId(request *dbapi.ConnectorNamespace) *errors.ServiceError GetOwnerClusterIds(userId string, ownerClusterIds *[]string) *errors.ServiceError GetOrgClusterIds(userId string, organisationId string, orgClusterIds *[]string) *errors.ServiceError CreateDefaultNamespace(ctx context.Context, connectorCluster *dbapi.ConnectorCluster) *errors.ServiceError + GetExpiredNamespaces() (dbapi.ConnectorNamespaceList, *errors.ServiceError) } var _ ConnectorNamespaceService = &connectorNamespaceService{} @@ -122,8 +123,6 @@ func (k *connectorNamespaceService) Update(ctx context.Context, request *dbapi.C return errors.GeneralError("failed to read updated connector namespace: %v", err) } - // TODO use signal bus and a namespace worker to sync connectors in updated namespace - // TODO: increment connector namespace metrics // metrics.IncreaseStatusCountMetric(constants.KafkaRequestStatusAccepted.String()) return nil @@ -204,7 +203,7 @@ func (k *connectorNamespaceService) List(ctx context.Context, clusterIDs []strin return resourceList, &pagingMeta, nil } -func (k *connectorNamespaceService) Delete(ctx context.Context, namespaceId string) *errors.ServiceError { +func (k *connectorNamespaceService) Delete(namespaceId string) *errors.ServiceError { dbConn := k.connectionFactory.New() dbConn = dbConn.Where("id = ?", namespaceId) if err := dbConn.Delete(&dbapi.ConnectorNamespace{}).Error; err != nil { @@ -221,7 +220,7 @@ func (k *connectorNamespaceService) Delete(ctx context.Context, namespaceId stri Where("namespace_id = ?", namespaceId). Rows() if err != nil { - return errors.GeneralError("unable find connector using namespace %s: %s", namespaceId, err) + return errors.GeneralError("Unable to find connector using namespace %s: %s", namespaceId, err) } defer rows.Close() for rows.Next() { @@ -232,14 +231,16 @@ func (k *connectorNamespaceService) Delete(ctx context.Context, namespaceId stri } if err := dbConn.Model(&connector).Update("namespace_id", nil).Error; err != nil { - return errors.GeneralError("failed to update connector: %s", err) + return errors.GeneralError("Failed to update connector: %s", err) } status := dbapi.ConnectorStatus{} status.ID = connector.ID status.NamespaceID = nil - if err := dbConn.Model(&status).Update("phase", "assigning").Error; err != nil { - return errors.GeneralError("failed to update connector status: %s", err) + if err := dbConn.Model(&status). + Update("namespace_id", nil). + Update("phase", dbapi.ConnectorStatusPhaseAssigning).Error; err != nil { + return errors.GeneralError("Failed to update connector status: %s", err) } } } @@ -278,3 +279,12 @@ func (k *connectorNamespaceService) CreateDefaultNamespace(ctx context.Context, } return k.Create(ctx, namespaceRequest) } + +func (k *connectorNamespaceService) GetExpiredNamespaces() (dbapi.ConnectorNamespaceList, *errors.ServiceError) { + var result dbapi.ConnectorNamespaceList + dbConn := k.connectionFactory.New() + if err := dbConn.Where("expiration < ?", time.Now()).Find(&result).Error; err != nil { + return nil, errors.GeneralError("Error retrieving expired namespaces: %s", err) + } + return result, nil +} diff --git a/internal/connector/internal/workers/namespace_mgr.go b/internal/connector/internal/workers/namespace_mgr.go new file mode 100644 index 000000000..a294b3472 --- /dev/null +++ b/internal/connector/internal/workers/namespace_mgr.go @@ -0,0 +1,66 @@ +package workers + +import ( + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/services" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/errors" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/services/signalbus" + "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/workers" + "github.com/golang/glog" + "github.com/google/uuid" +) + +var _ workers.Worker = &NamespaceManager{} + +type NamespaceManager struct { + workers.BaseWorker + namespaceService services.ConnectorNamespaceService +} + +func (m *NamespaceManager) Start() { + m.StartWorker(m) +} + +func (m *NamespaceManager) Stop() { + m.StopWorker(m) +} + +func NewNamespaceManager(bus signalbus.SignalBus, namespaceService services.ConnectorNamespaceService) *NamespaceManager { + return &NamespaceManager{ + BaseWorker: workers.BaseWorker{ + Id: uuid.New().String(), + WorkerType: "connector_namespace", + Reconciler: workers.Reconciler{ + SignalBus: bus, + }, + }, + namespaceService: namespaceService, + } +} + +func (m *NamespaceManager) Reconcile() []error { + glog.V(5).Infof("Deleting expired namespaces...") + namespaces, err := m.namespaceService.GetExpiredNamespaces() + if err != nil { + return []error{err} + } + + n := len(namespaces) + if n == 0 { + glog.V(5).Infof("No expired namespaces") + return nil + } + + glog.V(5).Infof("Deleting %d namespaces...", n) + success := n + var errs []error + for _, namespace := range namespaces { + id := namespace.ID + if err := m.namespaceService.Delete(id); err != nil { + errs = append(errs, errors.GeneralError("Error deleting namespace %s: %s", id, err)) + success-- + } + } + + glog.V(5).Infof("Deleted %d expired namespaces with %d errors", success, len(errs)) + return errs +} diff --git a/internal/connector/providers.go b/internal/connector/providers.go index e33c17a7c..af21bd428 100644 --- a/internal/connector/providers.go +++ b/internal/connector/providers.go @@ -58,6 +58,7 @@ func serviceProviders() di.Option { di.Provide(handlers.NewConnectorClusterHandler), di.Provide(routes.NewRouteLoader), di.Provide(workers.NewConnectorManager, di.As(new(coreWorkers.Worker))), + di.Provide(workers.NewNamespaceManager, di.As(new(coreWorkers.Worker))), di.Provide(workers.NewApiServerReadyCondition), ) } diff --git a/internal/connector/test/integration/feature_test.go b/internal/connector/test/integration/feature_test.go index 6ddaf5fc2..230d71f22 100644 --- a/internal/connector/test/integration/feature_test.go +++ b/internal/connector/test/integration/feature_test.go @@ -24,7 +24,7 @@ func TestMain(m *testing.M) { h, teardown := test.NewHelperWithHooks(t, ocmServer, func(c *config.ConnectorsConfig, kc *keycloak.KeycloakConfig) { c.ConnectorCatalogDirs = []string{"./internal/connector/test/integration/connector-catalog"} - c.ConnectorEvalDuration, _ = time.ParseDuration("30m") + c.ConnectorEvalDuration, _ = time.ParseDuration("2s") c.ConnectorEvalOrganizations = []string{"13640210"} kc.KeycloakClientExpire = true diff --git a/internal/connector/test/integration/features/connector-multitenancy-api.feature b/internal/connector/test/integration/features/connector-multitenancy-api.feature index bf69fd6ca..808ba23cb 100644 --- a/internal/connector/test/integration/features/connector-multitenancy-api.feature +++ b/internal/connector/test/integration/features/connector-multitenancy-api.feature @@ -111,23 +111,48 @@ Feature: connector namespaces API """ And I store the ".id" selection from the response as ${namespace_id} - # Delete eval namespace - Given I am logged in as "" - When I DELETE path "/v1/kafka_connector_namespaces/${namespace_id}" - Then the response code should be 204 - And I GET path "/v1/kafka_connector_namespaces" - And the response code should be 200 - And the response should match json: + # eval namespace MUST be in list of user's namespaces + Given I GET path "/v1/kafka_connector_namespaces/" + Then the response code should be 200 """ { - "items": [], + "items": [ + { + "cluster_id": "${connector_cluster_id}", + "href": "${response.items[0].href}", + "id": "${namespace_id}", + "kind": "ConnectorNamespace", + "name": "_namespace", + "owner": "${}", + "version": ${response.items[0].version} + "created_at": "${response.items[0].created_at}", + "modified_at": "${response.items[0].modified_at}", + "expiration": "${response.expiration}", + "tenant": { + "kind": "user", + "id": "${}" + }, + "annotations": [ + { + "name": "connector_mgmt.api.openshift.com/profile", + "value": "default-profile" + } + ], + } + ], "kind": "ConnectorNamespaceList", "page": 1, - "size": 0, - "total": 0 + "size": 1, + "total": 1 } """ + # Eval namespace should expire and get deleted after 2 seconds as configured in internal/connector/test/integration/feature_test.go:27 + Given I sleep for 3 seconds + And I GET path "/v1/kafka_connector_namespaces/" + Then the response code should be 200 + And the ".total" selection from the response should match "0" + # cleanup eval cluster Given I am logged in as "Gru" When I DELETE path "/v1/kafka_connector_clusters/${connector_cluster_id}" From 9ce5c97f5d985feb352ab64f61bdda46ea9e18ad Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Wed, 2 Mar 2022 22:12:19 -0800 Subject: [PATCH 09/12] fix(connectors): put back validateConnector validation call for connector patch --- internal/connector/internal/handlers/connector_validation.go | 4 ++++ internal/connector/internal/handlers/connectors.go | 2 ++ 2 files changed, 6 insertions(+) diff --git a/internal/connector/internal/handlers/connector_validation.go b/internal/connector/internal/handlers/connector_validation.go index 6da4718bf..9e9fffe72 100644 --- a/internal/connector/internal/handlers/connector_validation.go +++ b/internal/connector/internal/handlers/connector_validation.go @@ -15,6 +15,10 @@ func validateConnectorRequest(connectorTypesService services.ConnectorTypesServi return connectorValidationFunction(connectorTypesService, &resource.ConnectorTypeId, &resource.Channel, &resource.Connector, tid) } +func validateConnector(connectorTypesService services.ConnectorTypesService, resource *public.Connector, tid string) handlers.Validate { + return connectorValidationFunction(connectorTypesService, &resource.ConnectorTypeId, &resource.Channel, &resource.Connector, tid) +} + func connectorValidationFunction(connectorTypesService services.ConnectorTypesService, connectorTypeId *string, channel *public.Channel, connectorConfiguration *map[string]interface{}, tid string) handlers.Validate { return func() *errors.ServiceError { diff --git a/internal/connector/internal/handlers/connectors.go b/internal/connector/internal/handlers/connectors.go index a9d979628..3868b851f 100644 --- a/internal/connector/internal/handlers/connectors.go +++ b/internal/connector/internal/handlers/connectors.go @@ -197,6 +197,8 @@ func (h ConnectorsHandler) Patch(w http.ResponseWriter, r *http.Request) { handlers.Validation("connector_type_id", &resource.ConnectorTypeId, handlers.MinLen(1), handlers.MaxLen(maxKafkaNameLength)), // handlers.Validation("kafka_id", &resource.Metadata.KafkaId, handlers.MinLen(1), handlers.MaxLen(maxKafkaNameLength)), handlers.Validation("service_account.client_id", &resource.ServiceAccount.ClientId, handlers.MinLen(1)), + handlers.Validation("desired_state", (*string)(&resource.DesiredState), handlers.IsOneOf(dbapi.ValidDesiredStates...)), + validateConnector(h.connectorTypesService, &resource, connectorTypeId), handlers.Validation("namespace_id", &resource.NamespaceId, handlers.MaxLen(maxConnectorNamespaceIdLength)), } From da0efb99e7ecd8ffd8662ab0c3cf51bdfdc95dbe Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Thu, 3 Mar 2022 14:37:09 -0800 Subject: [PATCH 10/12] fix(connector): fixed namespace update operation by using ConnectorNamespacePathRequest type --- .../api/admin/private/api/openapi.yaml | 4 ++-- .../private/model_connector_namespace_meta.go | 2 +- .../model_connector_namespace_request_meta.go | 2 +- .../internal/api/public/api/openapi.yaml | 17 +++++++++++------ .../api/public/api_connector_namespaces.go | 8 ++++---- .../public/model_connector_namespace_meta.go | 2 +- .../model_connector_namespace_patch_request.go | 16 ++++++++++++++++ .../model_connector_namespace_request_meta.go | 2 +- .../connector/internal/generated/bindata.go | 4 ++-- .../internal/handlers/connector_namespace.go | 9 +++++++-- .../connector/internal/routes/route_loader.go | 2 +- openapi/connector_mgmt.yaml | 18 ++++++++++++------ 12 files changed, 59 insertions(+), 27 deletions(-) create mode 100644 internal/connector/internal/api/public/model_connector_namespace_patch_request.go diff --git a/internal/connector/internal/api/admin/private/api/openapi.yaml b/internal/connector/internal/api/admin/private/api/openapi.yaml index c481980eb..d374b2f2e 100644 --- a/internal/connector/internal/api/admin/private/api/openapi.yaml +++ b/internal/connector/internal/api/admin/private/api/openapi.yaml @@ -949,8 +949,6 @@ components: items: $ref: '#/components/schemas/ConnectorNamespaceRequestMeta_annotations' type: array - required: - - name type: object ConnectorNamespaceTenant: properties: @@ -972,6 +970,8 @@ components: allOf: - $ref: '#/components/schemas/ConnectorNamespaceRequestMeta' description: An evaluation connector namespace create request + required: + - name ConnectorAvailableTypeUpgradeList_allOf: properties: items: diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_meta.go b/internal/connector/internal/api/admin/private/model_connector_namespace_meta.go index 40b4deec2..208956202 100644 --- a/internal/connector/internal/api/admin/private/model_connector_namespace_meta.go +++ b/internal/connector/internal/api/admin/private/model_connector_namespace_meta.go @@ -18,6 +18,6 @@ type ConnectorNamespaceMeta struct { Owner string `json:"owner,omitempty"` CreatedAt time.Time `json:"created_at,omitempty"` ModifiedAt time.Time `json:"modified_at,omitempty"` - Name string `json:"name"` + Name string `json:"name,omitempty"` Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` } diff --git a/internal/connector/internal/api/admin/private/model_connector_namespace_request_meta.go b/internal/connector/internal/api/admin/private/model_connector_namespace_request_meta.go index 35bd142db..96ddef504 100644 --- a/internal/connector/internal/api/admin/private/model_connector_namespace_request_meta.go +++ b/internal/connector/internal/api/admin/private/model_connector_namespace_request_meta.go @@ -11,6 +11,6 @@ package private // ConnectorNamespaceRequestMeta struct for ConnectorNamespaceRequestMeta type ConnectorNamespaceRequestMeta struct { - Name string `json:"name"` + Name string `json:"name,omitempty"` Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` } diff --git a/internal/connector/internal/api/public/api/openapi.yaml b/internal/connector/internal/api/public/api/openapi.yaml index ab39a8b66..c4960afd6 100644 --- a/internal/connector/internal/api/public/api/openapi.yaml +++ b/internal/connector/internal/api/public/api/openapi.yaml @@ -1263,7 +1263,7 @@ paths: summary: Get a connector namespace tags: - Connector Namespaces - put: + patch: description: udpate a connector namespace operationId: updateConnectorNamespaceById parameters: @@ -1279,8 +1279,8 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ConnectorNamespaceRequest' - description: Data to updated connector with + $ref: '#/components/schemas/ConnectorNamespacePatchRequest' + description: Data to update namespace with required: true responses: "204": @@ -1918,8 +1918,6 @@ components: items: $ref: '#/components/schemas/ConnectorNamespaceRequestMeta_annotations' type: array - required: - - name type: object ConnectorNamespaceMeta: allOf: @@ -1943,17 +1941,24 @@ components: type: object ConnectorNamespaceRequest: allOf: - - $ref: '#/components/schemas/ConnectorNamespaceEvalRequest' + - $ref: '#/components/schemas/ConnectorNamespaceRequestMeta' - $ref: '#/components/schemas/ConnectorNamespaceRequest_allOf' description: A connector namespace create request required: - cluster_id - kind - name + ConnectorNamespacePatchRequest: + allOf: + - $ref: '#/components/schemas/ConnectorNamespaceRequestMeta' + - type: object + description: A connector namespace patch request ConnectorNamespaceEvalRequest: allOf: - $ref: '#/components/schemas/ConnectorNamespaceRequestMeta' description: An evaluation connector namespace create request + required: + - name ConnectorNamespaceList: allOf: - $ref: '#/components/schemas/List' diff --git a/internal/connector/internal/api/public/api_connector_namespaces.go b/internal/connector/internal/api/public/api_connector_namespaces.go index 6ec20088f..62c70cf5b 100644 --- a/internal/connector/internal/api/public/api_connector_namespaces.go +++ b/internal/connector/internal/api/public/api_connector_namespaces.go @@ -598,11 +598,11 @@ UpdateConnectorNamespaceById udpate a connector namespace udpate a connector namespace * @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). * @param connectorNamespaceId The id of the connector namespace - * @param connectorNamespaceRequest Data to updated connector with + * @param connectorNamespacePatchRequest Data to update namespace with */ -func (a *ConnectorNamespacesApiService) UpdateConnectorNamespaceById(ctx _context.Context, connectorNamespaceId string, connectorNamespaceRequest ConnectorNamespaceRequest) (*_nethttp.Response, error) { +func (a *ConnectorNamespacesApiService) UpdateConnectorNamespaceById(ctx _context.Context, connectorNamespaceId string, connectorNamespacePatchRequest ConnectorNamespacePatchRequest) (*_nethttp.Response, error) { var ( - localVarHTTPMethod = _nethttp.MethodPut + localVarHTTPMethod = _nethttp.MethodPatch localVarPostBody interface{} localVarFormFileName string localVarFileName string @@ -635,7 +635,7 @@ func (a *ConnectorNamespacesApiService) UpdateConnectorNamespaceById(ctx _contex localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept } // body params - localVarPostBody = &connectorNamespaceRequest + localVarPostBody = &connectorNamespacePatchRequest r, err := a.client.prepareRequest(ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes) if err != nil { return nil, err diff --git a/internal/connector/internal/api/public/model_connector_namespace_meta.go b/internal/connector/internal/api/public/model_connector_namespace_meta.go index 44edea726..aa66c88d7 100644 --- a/internal/connector/internal/api/public/model_connector_namespace_meta.go +++ b/internal/connector/internal/api/public/model_connector_namespace_meta.go @@ -18,6 +18,6 @@ type ConnectorNamespaceMeta struct { Owner string `json:"owner,omitempty"` CreatedAt time.Time `json:"created_at,omitempty"` ModifiedAt time.Time `json:"modified_at,omitempty"` - Name string `json:"name"` + Name string `json:"name,omitempty"` Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` } diff --git a/internal/connector/internal/api/public/model_connector_namespace_patch_request.go b/internal/connector/internal/api/public/model_connector_namespace_patch_request.go new file mode 100644 index 000000000..47fc9e0c3 --- /dev/null +++ b/internal/connector/internal/api/public/model_connector_namespace_patch_request.go @@ -0,0 +1,16 @@ +/* + * Connector Service Fleet Manager + * + * Connector Service Fleet Manager is a Rest API to manage connectors. + * + * API version: 0.1.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package public + +// ConnectorNamespacePatchRequest A connector namespace patch request +type ConnectorNamespacePatchRequest struct { + Name string `json:"name,omitempty"` + Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` +} diff --git a/internal/connector/internal/api/public/model_connector_namespace_request_meta.go b/internal/connector/internal/api/public/model_connector_namespace_request_meta.go index 6720545ac..464990f84 100644 --- a/internal/connector/internal/api/public/model_connector_namespace_request_meta.go +++ b/internal/connector/internal/api/public/model_connector_namespace_request_meta.go @@ -11,6 +11,6 @@ package public // ConnectorNamespaceRequestMeta struct for ConnectorNamespaceRequestMeta type ConnectorNamespaceRequestMeta struct { - Name string `json:"name"` + Name string `json:"name,omitempty"` Annotations []ConnectorNamespaceRequestMetaAnnotations `json:"annotations,omitempty"` } diff --git a/internal/connector/internal/generated/bindata.go b/internal/connector/internal/generated/bindata.go index 4b9e8231a..4b4071277 100644 --- a/internal/connector/internal/generated/bindata.go +++ b/internal/connector/internal/generated/bindata.go @@ -78,7 +78,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _connector_mgmtYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x7b\x73\xdb\xb8\xb5\xf8\xff\xfe\x14\xf8\x29\xbf\x8e\xdb\x7b\x23\x59\x92\xe5\x97\xe6\xa6\x33\x8e\xed\x64\xbd\x9b\x38\x59\xdb\xd9\x6c\xda\xe9\xc8\x10\x09\x49\x88\x49\x80\x06\x20\x25\x4a\x7b\xbf\xfb\x1d\x00\x7c\x00\x24\x48\x51\xb2\x62\x3b\xbb\xe2\x4c\xbb\x31\x09\x1c\x1c\x1c\x1c\x9c\x37\x20\x1a\x21\x02\x23\xdc\x07\xbb\xad\x76\xab\x0d\x9e\x01\x82\x90\x0f\xc4\x04\x73\x00\x39\x18\x61\xc6\x05\x08\x30\x41\x40\x50\x00\x83\x80\x7e\x01\x9c\x86\x08\x9c\x9f\x9e\x71\xf9\xea\x96\xd0\x2f\xba\xb5\xec\x40\x40\x0c\x0e\xf8\xd4\x9b\x86\x88\x88\xd6\xd6\x33\x70\x1c\x04\x00\x11\x3f\xa2\x98\x08\x0e\x7c\x34\xc2\x04\xf9\x60\x82\x18\x02\x5f\x70\x10\x80\x21\x02\x3e\xe6\x1e\x9d\x21\x06\x87\x01\x02\xc3\xb9\x1c\x09\x4c\x39\x62\xbc\x05\xce\x47\x40\xa8\xb6\x72\x80\x18\x3b\x0a\x6e\x11\x8a\x34\x26\x29\xe4\xad\x67\xa0\x11\x31\x3c\x83\x02\x35\x9e\x03\xe8\xcb\x59\xa0\x50\x36\x16\x13\x04\x1a\x1e\x25\x04\x79\x82\xb2\x41\x38\x0e\x45\x33\x6e\xd9\x9a\xc3\x30\x68\x80\x11\x0e\xd0\x16\x26\x23\xda\xdf\x02\x40\x60\x11\xa0\x3e\x38\x49\x3a\x80\x2b\xc4\x66\xd8\x43\xe0\x55\x80\x90\x00\x6f\x21\x81\x63\xc4\xb6\x00\x98\x21\xc6\x31\x25\x7d\xd0\x6e\x75\x5a\xed\x2d\x00\x7c\xc4\x3d\x86\x23\xa1\x5e\x2e\xe8\xaf\xe7\x73\x89\xb8\x00\xc7\xef\xcf\x25\x9a\xa1\xfa\x00\x52\x44\x79\x6b\x8b\x23\x26\x07\x91\x58\x35\xc1\x94\x05\x7d\x30\x11\x22\xe2\xfd\x9d\x1d\x18\xe1\x96\x24\x36\x9f\xe0\x91\x68\x79\x34\xdc\x02\x20\x87\xc0\x5b\x88\x09\xf8\x6b\xc4\xa8\x3f\xf5\xe4\x9b\xbf\x01\x0d\xce\x0d\x8c\x0b\x38\x46\x8b\x40\x5e\x09\x38\xc6\x64\xec\x04\xd4\xdf\xd9\x09\xa8\x07\x83\x09\xe5\xa2\x7f\xd8\x6e\xb7\x8b\xdd\xd3\xef\x59\xcf\x9d\x62\x2b\x6f\xca\x18\x22\x02\xf8\x34\x84\x98\x6c\x09\x38\x8e\x09\x40\x60\x68\xad\xcb\xf5\x3c\x42\xbc\xd8\xbf\xd1\x70\xb5\xae\xdd\x10\x9c\x04\x53\x2e\xd0\x12\x1d\xe2\xf5\x75\xb6\xdf\x8a\xa0\x98\x28\xfc\x9f\xc9\xff\x01\x67\xb7\x67\x5b\x5b\x00\x34\xe4\x32\xec\xd8\x6c\xba\x33\xeb\x34\xfa\x0a\xee\x18\x09\xfd\x0f\x00\x12\x82\xe8\xa7\x59\x82\x08\x90\x7b\x91\x41\x89\xc8\xb9\xdf\x97\xfd\x7f\xd3\xec\xfa\x16\x09\xe8\x43\x01\xe3\x56\x7c\x1a\x86\x90\xcd\xfb\xe0\x12\x89\x29\x23\x5c\xed\x96\x98\xb3\x41\x68\xb7\xb5\x26\x57\xa3\x3d\x43\x3c\xa2\x84\x23\x03\xdd\x46\xb7\xdd\x6e\x64\x7f\x02\xc9\xee\x02\x11\x61\xbe\x02\x00\x46\x51\x80\x3d\x85\xfc\xce\x67\x4e\x89\xfd\x15\x00\xee\x4d\x50\x08\xf3\x6f\x01\xf8\xff\x0c\x8d\xfa\x60\xfb\xd9\x8e\x47\xc3\x88\x12\x44\x04\xdf\xd1\x6d\xf9\x4e\x6e\xfa\xdb\x46\x67\x6b\x5e\xbf\xe5\xe7\x92\xae\x5d\x91\xf3\xaa\x16\x6e\xe7\x16\x8e\x6e\xe1\x20\x7b\x2f\x64\xa7\x9d\x7f\xdb\x2f\x06\xd8\xff\xdf\x98\x1e\x11\x64\x30\x44\x22\xde\xef\x7a\x6d\x35\xab\x15\xba\x6c\x39\x31\xbf\x9e\x20\x80\x7d\x40\x95\xc4\xcc\x3a\x01\xd9\x69\xab\x9c\x74\xf2\x73\x1f\x70\xc1\x30\x19\xa7\xaf\x31\xe9\x03\xc9\xba\xe9\x0b\x86\xee\xa6\x98\x21\xbf\x0f\x04\x9b\xa2\xfa\x3c\x99\x6d\x52\x00\x38\xf2\xa6\x0c\x8b\xb9\xd9\xf2\x25\x82\x0c\xb1\x3e\xf8\x27\xf8\x57\x09\xdf\xa6\xb0\x24\xa8\x97\xf3\xf3\xd3\x3c\xe7\xbe\x46\x02\xc0\xdc\x7c\xa5\x16\x49\xe9\x64\x51\x69\x61\xeb\x47\xe2\xda\x86\x93\x6b\xad\xc9\x37\x72\x5d\xd1\x57\x18\x46\x81\x89\x68\xf2\x58\xdd\xce\x74\xb3\x62\x2b\xf7\xd0\x09\xd4\x1d\x17\x90\x46\xd9\xb6\xb9\x2e\xb0\x1c\x08\xa1\xf0\x26\x52\x5d\x48\x76\x94\xfc\x83\x94\xe4\x8f\x49\xda\x6b\x77\x1e\x87\xa4\x67\x8c\x51\x56\x9f\x94\xbd\x76\x67\x55\x02\x66\x5d\x4b\xc9\x76\x3c\x15\x13\x20\xe8\x2d\x22\xd2\x20\xc0\x64\x06\x03\x63\x7b\x37\x7a\xed\xde\x0f\x42\xa4\xde\xea\x44\xea\x2d\x22\xd2\x05\xcd\x78\x29\xc7\x63\xe8\x2b\xe6\x82\x67\x04\xdb\x7b\xac\x8d\xba\x24\xc1\xf6\xda\xed\x55\x09\x96\x75\x2d\x25\xd8\x07\x82\xbe\x46\xc8\x13\xc8\x07\x48\xe2\x05\xa8\xa7\xac\x2a\x7f\x69\x7d\xb5\x8c\xf9\xb1\x66\x51\xcf\xcb\x2c\x14\x08\x02\xcc\x85\xd4\x73\x36\x33\xf0\x2a\x33\x65\x51\xa7\xa2\xf6\x95\x28\xbb\x16\x22\x6b\xb9\x13\xc1\xb1\xb1\x08\x0b\x9b\x73\xfc\x6d\x99\xe6\x94\xf9\x88\xbd\x9c\x2f\x33\x00\x82\xcc\x9b\x34\x9e\xbc\x22\x7b\x83\xb9\x28\x17\x89\x0b\x56\x6a\xa3\x3b\xea\xe9\x8e\x8d\x28\x5c\x28\x0a\x73\x76\xfd\x92\x16\x7d\x22\x1c\x23\xe9\xf1\x2e\x92\x8e\xf7\x10\x8c\x1e\x43\x50\x20\x13\x4b\x60\x8a\xc5\x13\xf5\x59\x05\x47\xbe\x64\x5b\xc6\x25\x0b\x2b\x5b\xba\x05\xa0\xf4\x03\xee\xa6\x88\xcd\x0d\xfa\x6a\xa7\x04\xf2\x39\xf1\xca\xa8\xfe\x1e\xb1\x11\x65\xa1\xb2\xfc\xa0\x8a\x3e\x00\x4c\x00\x24\xba\xd7\x84\x51\x42\xa7\x1c\x84\x90\x10\xc4\xb6\xaa\xb9\x4d\xbb\x27\x43\x4a\x03\x04\x89\xf1\xc5\xe1\x90\x80\xc4\xca\x7c\x49\x7d\x83\xc0\x25\x61\x19\xc3\x51\x75\x6e\x8e\xea\xad\xe1\xde\x18\xb5\x24\xe0\xa5\x46\xd2\xde\x21\x65\xfb\x23\xed\xa5\x17\xaf\x74\xa7\xd4\xb3\xe4\x2d\x20\x8d\x2a\xe7\xae\x4c\x7d\x74\x1f\x59\x7d\x94\x4b\x43\xcf\x43\x91\x40\x96\xf1\xfc\x63\x08\xc0\x5e\xbb\xad\xd6\x05\x53\xb2\xba\xb6\xc8\x83\x28\xa5\xd3\x6f\x52\x4b\xa8\x96\x5a\x20\xf2\x4c\x22\x1a\x94\xdb\xe8\xd7\x8d\x6f\x56\xcb\x37\xbb\xce\x7c\x7b\xe4\x4b\x99\x41\xa7\xcc\x43\xc0\xa7\x88\x93\x6d\xa1\xfd\xb3\x8d\x4d\x92\x63\x2c\x02\xa6\x65\x66\x89\xd6\xf6\x49\xd4\xc4\x56\xd2\x75\xbc\xb0\x7b\xd8\x19\xd2\xec\x2e\xc2\xf9\x83\x79\x5f\x4f\xda\x37\x5a\xd6\x2f\xda\xb8\x44\x1b\x97\xe8\x71\xa2\x43\x7c\xe7\xdf\xd5\x99\x8b\x05\x9b\x11\xfb\x8d\x87\x10\x69\x66\x4c\x29\x2f\xd0\x72\x89\x00\x97\xf8\x72\x37\x79\x9a\xb2\xa3\x66\x64\x7e\x13\x94\xdf\x18\x7e\x75\x88\xb4\x09\xca\x2f\x45\xb0\x7b\x89\x5d\xdd\x34\x40\x02\x7d\x4f\x59\xa8\x47\x28\x15\x87\xa7\xea\xf3\x22\x89\x58\xda\xca\x2d\x14\x9f\xca\x46\x71\xcc\x61\xe3\xee\xfe\x61\xa5\x9e\x5e\xe0\x7b\xc8\x3e\x0b\x40\x95\x04\x54\x56\x51\xa2\x46\xc1\x17\x2c\x26\x80\x47\xc8\xc3\x23\x8c\x7c\x70\x7e\xfa\x23\x4b\xc2\xfb\x11\x31\x0f\x60\x45\xa9\x18\x49\x0d\xf3\x3d\x85\xa2\x1a\xa0\x54\x26\xbe\x97\x5f\x17\x89\xc4\xb2\x46\x8b\x63\xd1\xa7\x50\x40\x20\xa8\x46\x22\x57\xb4\x23\x79\x69\xab\x82\x4d\x4c\x26\x09\x11\x1b\xa3\xa6\x82\xf2\xdf\x75\x23\xd5\x3a\xac\x4e\x87\x9f\x91\x27\x4a\xc0\x4a\x50\x4b\x42\xcd\x39\xac\x3f\x5f\xbd\xbb\xd0\xf4\x79\x0e\x2e\x5f\x9d\x80\xfd\xa3\x76\x17\x34\xd3\xba\x43\x41\x69\xc0\x5b\x18\x89\x51\x8b\xb2\xf1\xce\x44\x84\xc1\x0e\x1b\x79\xb2\xd5\x6a\xd8\xae\x3f\x44\x9f\x76\xfe\x23\x84\xc8\x37\x9e\xc0\x9f\x57\x27\x6e\x3c\x81\x1f\xc1\x13\x70\xd4\x9a\xc6\xe5\xc8\xcb\x56\x9b\x7a\x71\x15\xf3\x32\x39\x6a\xbb\xf4\xb9\x3a\x0b\x9d\xa1\x05\x6a\x6b\xde\x05\x29\x6b\xe0\x59\x30\x6b\xa4\xae\x73\x3d\xfe\x74\x29\xec\x78\xfa\x8f\x97\xca\x8e\xb9\x60\xc5\x8c\xb6\xee\xbc\x9e\xc4\xb6\x03\xd6\x0f\x99\xdf\x8e\x27\xb2\x49\x73\x6f\xd2\xdc\x06\xe5\x36\x36\x4e\x0d\x22\x6d\xd2\xdc\x4f\xcb\xcc\x59\x21\xcd\x6d\x29\xf4\x5a\x45\xc7\x39\x93\xe5\xbe\x69\xef\x3c\xb8\x3a\xd9\x6f\xcf\xee\x53\x3b\x01\x9e\xeb\xf7\xd0\x15\xc8\x4f\x33\x8d\x15\x2f\xc0\xd2\x15\xc2\x39\x62\x6e\xa4\xfb\x26\x23\xfe\xc0\xe7\x25\x12\x0e\x34\x8f\xf8\xc5\xef\x96\x3c\xe5\x97\xf5\x72\x3b\x00\x65\x07\xfd\x6c\x6f\xe8\xe1\xcf\xfa\xdd\x5f\x16\x9b\xf9\xfa\x9c\x87\x59\x76\xda\xaf\xc2\x69\xac\x6e\xfa\xa4\xe5\x5f\xcd\x18\x5e\xe2\x00\x6e\x62\x79\x1b\x3b\xb7\x0e\x91\x56\x8c\xe5\x25\x6c\xb6\x89\xe9\xad\x9a\xc7\x9a\x3e\x88\xf8\x9c\x46\xbe\x23\x46\xf7\x72\x7e\xee\xe7\xa5\xe8\xd4\x8f\xa0\x9d\xc7\xaf\x12\xa4\x0b\x5b\xd7\xcf\x75\x69\x14\xfd\x15\x33\x5d\x0f\x12\xbc\x5a\x22\x5a\x64\x8b\x0c\x3b\x4a\x17\xef\x19\x2e\xa0\x98\xaa\xfb\x51\xe2\xa9\x6f\xe4\xf2\x46\x2e\xaf\x59\x2e\x6f\x44\xb2\x8b\x66\x6b\x29\xb8\x5a\x83\x54\xce\x15\x5e\x95\xd8\xb5\xc5\xca\xaa\x2a\x89\xbc\xb0\xf5\xa6\x1e\x6b\x23\x17\x9f\x08\x91\x1e\xb0\x1e\x2b\x0d\xcc\x6e\x4a\xb1\xd6\x59\x8a\xb5\xbe\x28\xc8\x0e\xf4\x7d\x4a\x06\x59\x14\x64\x13\x16\x59\x2d\x2c\x72\x2c\xe9\xf8\x3e\xa5\x5a\x5e\x9b\x94\x84\x3e\xb6\x39\x50\x0b\x60\xd0\xdb\xa5\x5d\x96\xee\xfd\xa4\x62\x29\x36\x69\x2a\x23\xc9\x92\x65\xb2\xc9\x00\x31\x81\x02\xf0\x09\x9d\x06\x3e\x18\x22\x30\xe5\xfa\xb6\x41\x8f\x92\x11\x1e\x4f\x19\x52\x8c\xa5\xef\xe9\x33\x3d\x18\x4d\x14\x4a\x34\xdf\x69\x5a\xb5\x36\xea\xec\x8f\xaa\xce\x36\xe1\x97\x1f\xc9\xd6\x5f\xa3\xee\x92\x0a\x89\x47\xd0\x43\x3f\xb8\xd6\x5a\x32\xaf\xb8\x54\x56\x71\xd9\x5b\x8d\x96\xba\xd3\xe8\xf1\xd4\xed\x45\xba\xf4\xf5\x35\x2d\xc9\xf7\xa9\xa9\x63\x0b\xfd\x9e\x94\x76\x4d\x29\x93\x92\x64\xa1\x86\xcd\x26\x04\x66\x98\xe3\x61\xa0\xae\x13\x9e\x72\xc4\x00\xde\x28\xcd\x8d\xd2\xdc\x28\xcd\x27\xa8\x34\xf3\x75\xc8\x96\x04\x5c\xaa\x14\xb9\xa8\x36\x6b\x15\x23\x17\x44\x6e\x55\x39\x72\xda\x78\x29\xa9\xbf\xa8\x20\x99\xe4\xa0\xd6\x29\x49\xce\xf7\x59\xa6\x9e\x37\xed\xfb\x78\x15\xbd\x29\x21\x57\xab\xe9\x4d\xbb\xaf\xa5\xaa\xd7\x0d\xed\x87\xac\xeb\x4d\xa7\xb2\xa9\xec\xdd\x54\xf6\x1a\x94\xdb\x58\x0f\x35\x88\xb4\xa9\xec\x7d\x5a\x86\xc3\x2a\x95\xbd\xb6\x5e\xac\xe5\xc9\x15\x9d\xae\x7b\x56\xf7\x96\x7b\x71\x55\x75\xba\xd5\x7e\xdc\x52\x3d\x37\xb7\x0c\x67\xcf\x13\x70\x4e\x5d\x85\xc4\x85\x35\xdb\xa8\x93\x4d\x29\xf1\x03\x07\x22\x33\x1e\x34\x43\x91\xe9\xdb\x25\xcb\x89\xcd\x7e\x6e\x0f\xa4\x2c\x06\x99\xf7\x65\x1e\x3e\x77\xb6\x0e\x15\x60\x86\xf3\x0a\x7e\x62\x59\x18\xaf\xd2\xf5\x5b\xd4\xf8\x89\xcb\xc4\x9a\xc5\xc5\x99\x37\xba\x29\x2f\xde\x18\xdb\x75\x88\xb4\x62\xa8\x2e\x63\xb4\x4d\xb0\xee\x7b\x16\x18\xaf\x43\x98\xe6\x4a\x8c\x53\x90\x35\x8b\x8c\x2b\xc5\x6a\x8d\xf6\x3f\x64\xa1\x71\x79\x4c\x6d\x3d\xa5\xc6\x29\xfc\x4d\xb1\xf1\x46\x4a\x3f\x80\x94\xde\x08\x68\x17\xd5\xd6\x53\x6e\xbc\x0e\x19\x9d\x2b\x38\x2e\xb5\x79\x1d\x45\xc4\x95\xf2\xb9\x46\xfb\x4d\xd9\xf1\x46\x42\x3e\x11\x22\x6d\xca\x8e\xff\x44\x65\xc7\x46\xc4\x04\xcd\x60\xb0\xf6\x44\xf3\xd9\x0c\x06\x53\xf5\x6e\x9d\x99\x66\x3e\xa1\x4c\x80\x00\xcf\xe4\xdc\xd3\x11\x56\x4a\x40\xd7\xea\xfe\xa3\xe6\xa2\x25\xf5\xef\x99\x8f\x96\x20\xd6\x9b\x93\x2e\x40\xdc\xe4\xa5\xbf\x37\xd6\x9b\xbc\xf4\x83\x51\x6e\x63\x62\xd4\x20\xd2\x26\x2f\xfd\xb4\x5c\xb0\xfb\xe5\xa5\xb7\xb2\x51\x25\x72\xf1\x0c\xfb\xfa\x0e\xe0\x67\xfa\xff\xc1\x09\x0d\x43\x4a\xe2\x57\xea\x3f\x6f\x70\x66\x64\xa4\x82\xdf\x30\x06\x6e\x31\xf1\x8d\x3f\x23\x38\x46\xc6\x9f\x1c\x7f\x33\xff\x14\x54\xc0\xc0\xf8\x1b\x0b\x14\x26\x66\x89\xe3\x12\xe4\x88\x49\x5b\x45\x60\x93\xd4\x72\xbc\x85\x09\x1a\x89\x45\xb1\x11\x26\x02\x8d\xcd\x9a\x73\xfc\xad\x46\x2b\x85\x73\x79\x33\xf5\x41\xb1\x49\xd2\x06\x06\xc1\xbb\x91\x49\xa2\x2a\x06\x7b\xa7\xe6\x7b\x89\x46\x88\x21\xe2\x59\x99\xed\x92\x5b\xa1\x5d\x44\x01\x6a\x4f\xf8\x05\xae\x73\x12\x07\xa8\x95\x84\x8e\x1d\x52\xda\x3c\x35\x19\x07\xd8\xaf\xec\xa4\xbe\xe5\xe6\xd4\x5f\x6e\x81\xf1\xe2\xe5\xad\xc5\x03\x13\x49\xf5\xb2\x46\x06\x9e\x6f\x91\x80\x4b\xa2\x48\xbf\x10\xc4\x16\x22\xa0\x4d\x6b\x7f\x00\x2d\x39\x35\xa2\x2c\x84\xa2\x2f\xcd\x4e\xd4\x14\x38\x44\x8b\xc0\x84\xd4\x57\xee\xd6\xaa\x70\xd4\xfb\x2b\xc4\x66\xd8\x4b\x82\x26\x98\x92\x2b\x24\xa4\xb4\xe0\x55\x5b\x1b\x9b\x1b\x7b\xca\x82\xfb\x2d\xda\x94\x39\x76\x91\x03\xc7\x63\xcf\xa3\x53\x52\x29\x73\xbc\x00\x23\x22\x06\x16\x7e\xf1\x3b\x8e\x3c\x86\xaa\xd6\x2e\xed\xbb\x78\xfd\x4c\x88\xd5\xa8\xff\x86\x18\xc7\x94\x48\x56\x92\xee\xc4\x03\x49\x02\xe4\x52\x35\x6a\x6f\x80\xc6\xf1\xfb\xf3\x18\x29\x5b\x7b\x61\xf9\x71\xd6\xb1\x5f\x4e\x34\x5a\x6e\x67\xb4\x91\x93\x32\x41\xa0\x39\xa8\xa0\xfe\x9a\x1a\xb8\xf2\x5d\x79\x5e\x67\x2e\x18\xa4\xf8\xe3\xc3\x85\xfe\xf1\xc4\x4a\x7f\x4d\xae\x5c\x2e\x96\x62\xac\xe9\x0a\x19\x83\xf3\xdc\x17\xa5\x98\x8a\x3a\x3c\xb7\xa0\xe6\xdc\x97\x5a\x5a\x4b\xe7\xc6\x7c\xcf\x4d\xad\xfb\x8b\x24\x47\xf9\x6e\xb5\xcc\x82\x9f\x68\xe0\xf3\x44\xed\xab\x93\x9c\xda\x4c\xd7\x47\x3b\x25\x04\xf9\x4f\xa8\x61\x82\x73\xc2\x05\x24\x1e\x6a\xad\xc2\xa3\xa5\x62\x24\x5b\x88\x67\xf1\xaf\x86\xc4\x61\x22\xcf\x58\x97\xac\x4d\x09\x4b\x3f\xb3\x57\x51\x4b\x05\x35\xf4\x25\x1a\x63\x2e\xd8\x7c\xcd\x24\x51\xc0\x41\x02\xfc\x01\x68\xa3\x1b\x03\x96\x8c\xb8\x2e\x2a\x25\xbc\xa4\x0e\x07\x5b\x9c\x64\x1f\x17\x76\x52\xab\x71\x9c\x3f\xf8\xdc\x58\xbb\xca\x9e\xc1\x60\xea\x30\xb6\x4c\x21\x5a\x3c\xd8\x5c\x86\x6d\x52\xd6\x96\x3f\xae\x6d\xa3\x6d\xee\xeb\xdc\x7e\xae\x7f\xbe\xba\x91\xb7\x8f\x8b\x17\xd7\xa7\xa4\xce\x9f\xaa\xbb\x12\x50\xe4\xac\x1f\x8b\x2a\x88\x4c\x43\x93\xbb\x7c\xcc\x63\xee\x44\xa6\x66\x63\x08\xfa\x73\xf7\x08\x71\xd4\xc8\x34\x61\x5c\xeb\xa3\x4a\xa6\x2a\x69\x5f\x02\xd8\xbd\x00\x7a\x4b\x4a\x0b\xc4\xac\x98\xc9\xb2\xd2\x00\xaa\xa0\x1a\x88\x02\x48\x90\x71\xf2\x4f\xa7\x6f\x1b\xab\x6c\xae\x8a\x89\x37\xdc\x33\x30\x69\xb2\x82\x1e\xd6\x90\xbf\x17\x72\x57\x8a\x12\x55\x4b\xc6\xad\x16\x25\x7b\xb1\xac\x73\x02\xa0\xe0\x0f\x2c\x33\x0b\xc5\xbd\xb9\x78\xa4\xe9\xe6\xd4\x67\xa6\x75\x9b\x43\xcb\xcc\xe2\x3e\xeb\x78\x15\xf3\xab\x73\x52\xa6\x7c\x5a\x6a\x62\xb6\xdd\xb2\xb4\x9b\xe7\xb4\x4c\x96\x36\x64\x96\xbb\xac\xd3\x2d\x02\x8d\xb7\x27\x13\x48\x08\x0a\x2a\x64\x9d\x8f\x46\x70\x1a\x08\xf9\x16\x0e\x03\x54\x22\x01\xe3\x8f\x36\xc1\x4f\x11\x97\x0e\xc0\xb2\xd2\x54\x8b\x4d\x13\x36\x8d\x22\x4b\xb0\xfa\x71\x8a\xd4\x1e\x6e\xd9\x71\x20\xe7\x78\x4c\x4c\x5d\x97\xbc\xb3\x06\x53\xa2\xd1\x6e\xb5\x18\xc3\x11\xc4\x41\x11\x65\x1b\x8a\x9f\x4b\xf4\x36\x25\xeb\xcc\xb0\x34\xfd\xf3\x0d\xad\x0f\x39\xae\x36\xed\xa4\xca\x78\x8f\xb4\xee\x4c\xa4\xb5\xd9\x33\x80\xda\x6d\x33\xbe\xe4\x7f\x31\xcc\x19\xcd\x91\xd0\x4c\xf6\xac\x62\xcc\x12\xa3\x38\xdb\x4c\x39\x5c\x8a\x70\xb7\xab\x2c\xb7\xd8\xf1\xdc\xce\xc0\xa9\xef\x83\xc4\x58\xab\x8b\xe6\x22\x8b\x35\xc3\x37\xa5\x90\x09\xfa\x99\x11\xbd\xab\x32\x0f\x65\x4b\xa5\x66\xf9\x04\x46\xc8\x7a\x1d\x31\xea\x21\xce\xcd\x5f\xd0\x96\xaf\x75\xc4\x70\x02\x89\x1f\xd8\x01\x1e\x4b\x04\xd9\x7c\xe1\xb0\x30\x5c\x5c\x21\x2d\x0c\xd7\xd2\x0f\x24\x68\xdb\x51\xf7\xf5\x76\x1e\x28\x05\xb5\xaa\xd1\x52\xa0\x60\x32\xd0\xc2\x1e\x66\xb9\xf8\x62\xf0\xb6\x5c\x5b\xb4\xf2\xb1\x18\xcc\x16\xd8\x9a\x6b\x6d\x28\x2e\xc1\x97\xd7\x42\x39\x03\x6d\x35\x63\xca\x32\x54\x96\xec\x6b\x09\x8c\x3c\x76\x8f\x61\x7c\x95\x4c\x66\x49\xf5\x9a\x64\x22\x06\x33\x1d\x3d\x71\x6b\xda\x7c\x8c\x58\x3f\x49\x48\x0e\x13\xb1\xdf\x73\x68\x95\x27\x6b\xf1\xad\xc1\xd4\x7b\x14\x1b\x6f\x1d\x8c\xbb\x64\x6f\xb7\x4d\xf8\x27\x30\x06\x1b\x6e\x23\x10\x5c\xcf\x23\x3b\x74\x95\x7e\x92\x5f\x9c\x0e\xe4\xdf\x9b\x29\x0a\x97\x28\x62\x88\xcb\x11\xad\xba\x3b\x55\x28\xcf\xa7\x51\x44\x99\x40\x3e\x18\xce\x95\xa3\x79\xfc\xfe\x3c\xee\x48\x09\xb2\x69\x5c\xd4\x49\xa0\xa8\x97\xf4\xab\x78\x63\xe7\xde\xea\xf9\xae\x13\xe2\x67\x4e\xc9\xc0\x02\xfb\x48\x89\xa1\xbc\x22\x2d\xac\xc7\x05\x0c\x51\xf1\x74\x94\x1c\xa5\x55\x25\x00\xcc\x0f\x25\xd2\xd2\xae\x20\xd0\x6d\xee\x39\x52\xac\x92\x0b\x6c\x6c\xd7\xf9\xc4\x8d\x96\x19\x6b\x5d\x1b\x26\x6f\x03\xe4\x91\xab\xc2\xfb\xd8\xfc\xb3\x80\x7c\x6d\x1a\x61\x8f\x92\x41\x3e\xff\x55\x18\xec\xc3\xe5\x1b\x15\x05\x25\xaa\xfd\xea\xa3\x05\x70\xb8\x68\x3d\xde\xa8\x26\xd9\x8d\x83\x50\xa0\x31\x65\xf8\x1b\xb2\x87\xbc\xef\xba\x94\x33\x0d\x8c\xe0\x10\x07\xb8\xb8\x39\x5c\x47\xc4\x8c\xc6\x45\x21\xe4\xc9\xf5\xfe\xae\xc8\xba\xeb\x14\xca\x24\x68\xf2\x1c\x2b\x81\x93\x04\x98\xd5\x55\x8f\x1e\x24\xe6\x3d\x8f\x33\x5d\xc1\x83\x00\x2c\x98\x91\x05\x68\xd9\x86\x19\x61\x14\xf8\x6e\x5e\x28\x48\x20\x60\x0a\xbd\x1f\x67\x02\x45\xb5\xf5\x27\xd0\xe7\x72\x9a\xa5\xc1\xed\x5c\xcd\xe8\x33\x9b\x42\xf9\x03\x3f\x4b\x38\x87\x35\x13\x0a\xb5\x7c\x3f\x48\x08\x15\xb0\x90\xd8\x73\x93\xcb\x41\xaa\x52\x26\x2e\x5b\x1d\xb7\x2a\xad\xd8\xc9\x8e\xb4\xc7\x82\x1e\x6e\xab\xc3\x69\x77\x28\xcb\x43\x82\xdf\x2a\x59\x9d\xc7\x70\xc2\x5c\xac\x91\x37\x96\xd3\x36\xd7\x88\x40\x22\x7e\x31\x8a\x39\x6a\x84\xdb\xa6\xdc\xf0\xbb\x9a\x80\xb2\x31\x24\x98\x2b\x36\xa8\x1e\xa7\x5f\xce\x82\x35\xea\x9a\xd2\x10\x46\x9d\x9a\xa4\xe5\x48\x95\x91\x21\x23\xb7\x1d\x9b\xb0\x44\xe7\x19\x16\x13\xc4\xf4\xfd\x82\x94\x59\x04\x00\xd8\x07\x3e\x8a\x10\xf1\x31\x19\x27\x37\xf6\x2a\x1e\x91\xda\xdd\x9a\x51\xa5\x33\x97\x5f\x45\xa7\x15\x7f\xec\x3c\x3c\xa5\x8b\x5e\x72\xa7\xa8\xeb\x44\x8c\x8a\xf7\x88\x5a\x6b\xb0\x5a\x80\xa3\xba\xc4\x7a\xf9\x12\xab\x14\xc9\x4a\xcf\xd8\xfc\x90\x67\x8d\xfb\xb1\x47\xc9\x32\x19\xb3\x73\x2f\x15\x59\x50\x40\xef\x5e\xb6\x7b\xd2\xbc\x8e\x08\xf8\x13\x28\x5a\xa3\x04\xbd\x84\x08\xf5\xb7\xd7\x9a\x1d\xc9\xe5\xf0\xbf\x67\x64\xcd\xa5\x3b\x97\xf5\x27\xeb\x47\xdf\xcc\x2f\x2b\x6c\x5b\xf4\x35\xc2\x76\x2e\x24\x79\x9e\x55\x15\xde\x55\xc2\x14\x96\x1a\x4a\x9e\x55\x84\x81\xe9\x5c\x96\xc5\x29\xb0\x9f\x7b\x51\x3b\x70\xe1\x10\xc5\xfa\x83\x9e\x80\xe4\xe2\xfc\xbd\x2c\x19\x65\xf5\xed\x2c\x69\xd7\x82\x73\x75\x7e\x2a\x55\x11\x43\x1e\x65\xe9\x81\xfa\x9c\xbb\xe0\x20\x60\xee\xc2\x15\xc7\x01\x14\xb3\xe2\x57\xe3\x60\x54\x22\xe7\x7f\x23\xdf\xfe\x25\x7c\x38\x46\x00\x13\x1f\x7d\x2d\x40\x1f\xc1\x80\xa3\xfa\x58\x16\xeb\xc2\xf3\x75\xc8\xda\x1c\x04\x8d\xb8\xa8\xce\x2c\x40\xd6\x48\x1b\xf5\xd2\x95\x48\x5f\x4c\xc3\xa1\xd4\xff\x23\x2d\x9a\x00\x26\x00\x41\x6f\x62\x4e\x7a\x8d\xd3\xc8\x17\x4a\xa7\xd3\x68\xb7\xf5\x44\xe2\x5b\xb6\x9c\x82\xec\x3f\x99\xab\x77\x15\x1f\x1d\xd4\xa5\x59\xaa\x93\x74\xab\x3d\x86\x05\x62\x18\xb6\x14\x87\xf0\x39\x11\xf0\xab\x0e\x47\x60\x9e\xb1\x1a\xc0\xdc\x40\x28\xc4\x01\x64\xd2\x29\x14\xb9\x2e\x08\xdc\x24\x80\x6f\x80\x17\xc0\x29\x57\xc6\x0f\x24\xe0\xea\xd7\x37\x3a\x46\x1e\x22\x22\x32\x87\xf0\x4c\xd2\x4d\x11\x3a\xf1\x37\x55\x7f\xed\xf1\x43\x32\x4f\xc1\x5a\xae\xd3\x8d\xf6\x2b\x79\x06\xe7\x15\x65\x09\xe9\x9e\x4b\xc4\x98\xba\x39\x4d\xca\x6a\xc3\xb1\x92\xe4\xe6\xe6\x00\x62\x82\xb0\x16\xf0\xcf\xa5\x49\xa7\x46\x1a\xd1\x20\xa0\x5f\xa4\x09\xa7\x27\x16\xd7\x78\xc9\xe7\xe6\xe6\x86\xdf\x05\x96\x9f\x04\x20\xf7\xcc\xef\x59\xe3\xeb\xe5\x91\x00\x03\x48\xfc\x41\x22\x18\xee\x83\xd2\xf3\x04\x48\x39\x7e\xe7\x9a\xb0\xe6\x0a\x93\x6d\xa1\xd3\xd7\x3e\xf2\x9f\x4b\xf3\x16\x8f\x0c\x23\x16\x73\x80\xc2\x48\xcc\x9f\xcb\x77\x99\xd8\xd2\x45\x48\x7c\x1a\x08\x0e\x20\xb3\xd6\x4f\x62\xd3\x4a\xf9\x3a\x0a\xa8\x8f\xac\x53\x6b\x45\x5e\xcf\xb1\xb2\xc9\xee\xc9\xd4\x1a\x25\x3b\x54\x6f\xe1\x18\xc0\x7d\x77\x21\x17\xf3\x00\xf5\x95\x56\xd3\xb2\x42\x5d\x4b\xe7\xde\x61\xd9\x06\x53\x8d\xb2\x0d\x65\xf0\x42\xf5\xce\x5a\xb0\xa3\xbe\x4c\x10\x43\xd6\x76\xca\x86\xb4\x76\x15\x38\x96\x7c\x82\xfc\x78\x77\x24\x17\xa0\x6b\xe4\xd5\xe2\xdc\x48\x2a\xdd\x3c\x07\x37\xc6\x14\xe4\x9f\x31\xb7\xc8\x7f\xaa\x80\xe2\xcd\x73\x00\x89\x0f\x6e\xe2\x78\xef\x4d\xb6\xd1\x92\x21\xf4\xa1\x04\xca\xf4\xa2\xdf\xfc\xcf\xdf\x65\xdf\x17\x37\x8a\x6d\x6e\xde\x9c\xff\x72\xe6\xe8\xe3\x51\xf2\x79\x4a\x3c\x81\x67\x28\xdf\xff\xf8\xe2\xf4\x46\x0f\xf9\xee\xf2\xa6\x05\x7e\xa2\x5f\xd0\x0c\xb1\xe7\x60\x4e\xa7\x4a\x30\xc8\x99\x43\x10\xc2\xaf\x38\x9c\x86\x92\x06\x9d\x76\x06\x8e\x12\x35\x57\x98\xcc\x54\xb1\x85\x41\xfe\xb3\x94\xcf\x5c\xbb\x33\x97\x4e\xd1\xe7\xac\x45\x7c\xb5\x3c\xb8\x81\x5f\x78\x93\xdf\xf1\xa6\xce\x4c\x6a\x24\x55\x28\x52\x93\x06\xdc\xe8\xb2\x99\x9b\xba\xdb\xd5\xde\xab\x2f\x80\x0d\x5f\x81\x4f\x40\xbf\xb0\xeb\x75\x54\xf7\x7f\x46\xcd\x7f\xb9\xa7\xa1\x2b\x8c\x71\x5c\x45\xab\xa7\x01\xf5\x28\xfa\xb7\x67\x04\x64\x82\xeb\xf7\x72\x56\x2b\x62\x1c\xe0\x5b\x24\x91\xfe\x4b\x77\xef\xbb\x08\x16\x25\x2e\xe5\x47\x7b\x59\x0c\x79\x03\x85\xfa\xae\x7c\xf0\x09\xe4\x20\x42\x2c\xc4\x9c\xc7\x25\xc6\x1c\x21\xc5\x52\x9a\x2e\xc8\x37\xf8\xe0\x82\x0a\xd4\x4a\xf0\xd3\x4a\x27\x3b\x23\x28\x39\x3e\x2e\xd2\xc0\xdc\xe8\x5d\x2e\xbe\x62\xa3\x41\xf1\x5c\x89\x50\x72\x0b\x20\x87\x8e\xb7\xe4\x0b\xc8\x8b\xbd\x5a\x5c\xd2\x58\x4d\xbc\x6d\x65\xc7\xcc\x55\xed\x4c\x82\x56\x7c\xce\xdc\x04\x8a\xfa\x60\xa8\xde\xc6\x2f\xf5\x1f\xaf\x62\x93\xfc\xe7\x8f\xd7\x5b\xe6\x88\x13\x21\x22\x09\xdd\x9e\x6d\xbe\xbe\xcd\x79\x6e\x3a\x17\xb8\xd3\x84\x6e\xbc\x9d\x5b\x3f\x60\x6d\x59\x04\xd5\x00\xb0\xdf\x07\x01\x1d\x0f\x38\x26\xb7\x83\x76\xab\x93\x7e\xd0\xc7\x1a\x2c\x48\xe9\xb7\xa5\x8e\x4c\xa8\x0a\x18\xbe\x63\x0e\xd2\xc8\xe1\xff\x86\x8e\xc1\x15\x26\xb7\xe9\xeb\xc4\xcd\x02\x0d\xab\xb5\x2b\xc7\xd6\xcc\x4b\x02\x3b\xc1\x93\x87\x9c\xa5\xa0\x56\xc4\xbf\x15\x91\x71\x86\x51\x31\xc7\xd4\x04\xdc\x1c\xaf\x2c\xc3\xd3\x54\x35\x52\x83\x7c\x8d\x54\xd3\x55\x23\x55\xcc\x5b\x94\x9f\x29\x09\xc3\xa2\x6b\x98\x6d\xb5\xec\x66\x84\xe4\x11\x58\x04\x7a\x05\x9c\xee\xa2\x23\x0a\x5d\x15\x87\x06\x20\x9c\x06\x02\x0f\x02\x4c\x9c\x27\x4d\xd3\x6a\x4b\x73\xcf\xdb\x0d\x8c\xc5\x7b\x2b\x61\x81\x37\x98\xb8\x5a\xc6\x88\x57\xb7\x51\x73\x18\x52\x1a\x20\x48\x1c\xdf\xbf\x36\xc7\x8c\x4e\xa3\x3e\x68\x20\xe2\x47\x14\x13\x51\x3c\xde\xc3\x27\xf4\xcb\x00\x06\xc1\xfd\xa7\x73\x35\xa1\x5f\xa4\xc2\x2f\x9f\x4c\x55\x8b\x7b\x4e\x45\xd0\x08\x7b\x0b\x92\xd3\x34\x0c\xa5\xa1\x20\xd5\x93\x40\x7e\x7a\x9a\x41\x6b\x4f\x05\x40\x47\x7c\xdc\x2c\x74\x5d\xde\xa0\x3c\x0f\x91\xa1\xad\x76\x9d\x8d\x33\x17\x28\xba\x7f\x30\x2c\x57\x93\x91\x3d\xcd\x4a\x46\x8e\x61\x12\x8e\x98\x18\x28\xab\xb1\xac\x4d\xb9\x5f\x59\x7c\x8e\x7d\x5f\x55\x94\x4c\xb9\xa0\xa1\x36\x46\x13\x73\xc4\xa3\xca\x3e\x11\xb1\xea\x8f\x0d\xde\x10\x71\xae\x03\x01\x40\x30\x48\x38\x16\xf9\x94\x61\xf6\x2c\x9e\x8e\x7c\x16\xcc\xa5\x30\x9f\xe4\xa7\x84\x12\xa3\x5b\x23\x2d\xa8\xf4\x48\xa1\xef\x23\xbf\x12\x54\xcc\x1c\xaf\x64\xa7\xea\x86\xe5\x4c\x62\x3e\x25\x89\xab\x4a\xec\xd3\x4c\x43\x8a\x7e\x1d\x94\x7f\x53\x39\xac\x7b\xa3\x5c\x96\x39\x33\x9f\xe6\x42\xac\x92\x94\xda\x02\x9c\xcf\x15\xbb\x6a\x6a\x83\x63\x65\xff\x97\x77\x29\x17\xf0\xf5\x30\x6f\x5a\xbb\xc3\xd9\x68\xc1\x18\x75\x76\x20\xfa\x2a\x18\xf4\x96\xdb\x82\x67\xba\x0f\x80\x31\xb3\x8e\x18\x0d\xd5\xe2\x0f\xa9\x9f\x97\x1a\xd9\xf3\xc7\xdf\x3e\xeb\xe0\xc5\x18\xa3\x84\xc4\x0f\xc5\x6a\x16\x1b\x7c\x2f\x5e\x9b\x40\x3e\x98\x20\xe8\x23\x36\x18\xe1\x40\xa0\x42\xa1\x69\xf6\x58\x6b\xfc\x4a\x35\x06\x43\xc8\xa5\xfb\xaf\x43\x0b\xba\x7e\xd0\x53\xeb\x4e\x09\x02\x1a\xee\x3d\x99\xcf\x9d\xe7\x2f\xc5\x4b\xf2\x9e\x1e\x37\x76\x76\x69\x92\x6f\xab\x16\x6c\xc9\x01\xee\xb8\xf3\x45\x31\xc5\x6f\x3f\x31\x4f\xfc\xa4\x87\x5a\xdc\x7c\x7d\xbc\xea\xa8\x3e\x28\xa2\x05\x79\x82\x5a\xbc\x50\xdf\x9f\x5d\x0b\x9c\x54\x8f\x65\x33\x17\xb0\xb6\xef\xf7\x76\xfe\x86\x8e\xcd\xac\x93\x75\x90\x00\x34\x8e\x86\x7c\xd6\xe6\x07\x82\xa0\x83\x71\xbb\x3b\x9e\xec\x8d\x7b\x86\xff\x52\x38\xe7\x62\xf4\xd9\x1f\xb2\x11\x6b\xb7\xbb\xd1\x88\xdc\x4e\xda\xa6\x69\x96\xdd\x55\x00\x1a\x9c\xcd\xbc\x26\xf4\x3c\xd1\xec\xec\x77\xd1\xa8\xeb\x1f\x36\xdb\xdd\xf6\x51\xb3\xd7\xe9\x1c\x34\x0f\x7b\xfb\xdd\xa6\x3f\xda\xdf\xf5\xba\xed\xee\x9e\xd7\xdd\x77\x40\x89\xef\x31\x00\x8d\x61\xa7\xd7\xf3\x8f\x8e\x3a\xcd\xf6\x21\x1a\x36\x7b\xbd\x83\x6e\xf3\x10\x79\x9d\x26\x1a\xb6\x77\x7b\xde\xfe\x51\x77\xb7\x33\x34\xfb\x4f\x59\xd0\x07\x8d\x11\xa5\x4d\x17\xbe\xad\x5b\xc8\x5b\xd0\x0b\x51\xcb\xa3\x61\xbf\xd7\xdb\x6d\xe4\xfc\x29\xe7\xf9\x19\x63\xfa\xed\xdb\xc3\x80\x8c\xdb\xbb\x1d\x8e\x8e\xee\x6a\x4c\x1f\xb5\xbb\x7b\xdd\xfd\x3d\xd4\x84\x87\x87\xb0\xd9\xeb\x8d\x86\xcd\xc3\xde\x5e\xbb\x89\xfc\x76\xa7\x8d\x86\xfb\x43\x6f\xcf\xab\x9a\xbe\xef\xed\xc1\xc3\xee\xd1\x61\x73\x88\xfc\x83\x66\xaf\xdb\x45\xcd\xc3\xa3\xde\x41\x73\xb4\x3f\xf2\xe1\xfe\x51\xf7\xa8\x3b\x1a\x15\xa7\x3f\x84\x2c\x9e\x7e\x37\x1c\x79\xb0\xdd\xee\x8a\xa3\xbb\x03\x3e\x6e\x71\x56\x36\xfd\xe4\xf0\x48\xde\x71\x2e\x9e\x59\x01\x0d\xb7\xd7\xee\x3c\x1f\xe4\xf2\x3d\x53\xe7\xc9\x0c\x0e\xe9\x27\x73\x14\x79\xe1\x6b\xec\xac\xa8\xc5\x7d\x3e\x84\xf6\x65\xa2\xa9\xdb\x6c\x0f\x75\x8b\xe6\xf9\xed\x98\x64\x44\x1b\x57\xd7\x97\xe7\x17\xaf\x6d\xe7\xc2\x69\x48\xa6\x3d\x7e\xbe\x7a\x77\x91\xbb\xc4\x21\xf6\xca\x0b\xf9\xcc\x4a\x0f\x21\x8e\xcf\xa8\xaf\x52\x2e\x16\xfd\xcb\x24\x9a\xa5\x9a\x28\x9b\xb3\xec\x98\x4e\xee\x5c\x9e\x0a\xc8\x0d\x92\x53\x53\x76\x59\x0e\xf4\x07\x01\x12\x02\xb1\xc1\xdd\x14\xe5\xa7\xa9\xa8\x2b\x19\x2e\xb8\xcb\x85\x8b\xaa\x7f\x46\xae\x2c\xf4\xe4\xb8\xcb\xce\xc8\x6c\x2f\x92\x40\x25\xd5\x72\xc9\xef\x69\x34\xec\xf8\x4c\x0b\x46\xb8\x45\x23\x44\xf8\x04\x8f\x84\xe4\xed\x9d\x88\xd1\x11\x0e\x90\x6b\x75\x41\x23\xf6\xd0\x9b\x56\xa3\x25\xae\x29\x2c\x9b\xb3\xec\xe0\x98\xf7\x23\x4c\xa6\xfc\xb6\x3d\x47\xa4\xaf\xd1\x69\x1b\x82\x20\xbe\xb9\x24\x77\x97\x58\x75\x70\x4c\xdf\xb1\xb7\x63\xc1\x51\x37\x3c\x81\xc6\xc9\xbb\x8b\x8b\xb3\x93\xeb\x77\x97\xcd\xb7\xaf\xdf\x5e\x37\xad\x26\xf1\xbd\x4e\xa0\x71\x35\x27\xde\x84\x51\x42\xa7\x1c\x40\x4f\x17\x87\x71\x40\xa8\xc8\x2a\x9a\x75\xf0\x1d\xf2\x39\xf1\x5e\x48\xc1\x50\xbc\xfe\x21\x77\xf1\x13\x68\x74\xf0\xc7\x73\x1c\xde\xbd\xf6\xd8\xe9\xf4\xcd\x7e\x07\x7e\xf8\x7a\xfe\x8f\xbb\x97\xd7\x77\x17\x97\x30\xa5\xd2\xb9\x0e\x66\xff\x3a\x45\x6c\x5e\x83\x52\xdd\x35\x51\xaa\xbb\x90\x50\x5d\x07\x9d\xfe\x63\x30\xc0\x2b\x75\xd8\x56\x1a\x6f\x11\x64\x1c\x59\xa9\x9c\x3e\xf8\x40\x60\xfc\xcb\xbe\x2a\x5e\xa3\x83\x35\x71\xbd\x14\x57\x77\x21\xc0\x08\x0f\x74\x4c\x33\x3e\x87\xda\x07\x05\x0c\xfa\x4b\x8c\x97\x95\x9e\x7b\x34\x98\x86\x44\xdb\x96\x72\xa4\x38\x56\x0f\xb6\xb1\xbf\xdd\x02\x57\xae\x76\x2a\xa9\x65\x8e\x26\xb5\x30\x25\xcf\xe3\x54\xb3\x17\xd0\xa9\x3f\x88\x13\x22\x2c\x79\xab\x8f\xa0\xb5\xc0\xaf\x3a\x31\xa1\x17\xb2\x0f\xb0\x0f\x5e\x80\x4e\x77\xb7\x94\x2b\x82\x8f\xa7\xaf\xa7\xf3\xe1\x39\x3b\x23\x5f\xd9\x31\x0a\x0f\xba\xbd\xf1\xdd\xed\x2d\x3e\x9d\x25\x5c\x91\xbf\x4a\xd0\xc5\x09\xbd\x76\x6f\x2d\x9c\x70\xb0\x88\x11\x0e\x1c\xfb\xa5\xce\x7d\x84\xe9\x64\x9c\x57\x07\xbb\xa6\x74\xf0\x78\x13\x3a\xb1\x7e\x18\x02\x60\xff\xc5\x76\x07\xff\xb2\xeb\x4f\x7f\xfb\x74\x3e\x9b\xed\x7d\x9a\xbd\x09\xe6\xdf\x3a\xe1\xeb\xcb\xdd\x9f\xe7\x77\x17\xdb\x4a\x34\x8c\xe8\xd4\x2c\x42\x2d\x6c\xfe\x4f\xef\x0e\xc6\xdd\xf1\xfe\x4f\xd7\xfe\x87\x5f\x3e\xc0\xee\x2d\xff\xe9\xb0\x7b\xfb\xeb\xe9\xee\x3c\xa1\x4c\xfe\x5a\x4d\xa7\x68\xec\xac\x47\x32\x76\x16\x0a\xc6\x8e\x83\x2c\xd9\x36\x9e\x21\x86\x47\x73\xf0\xf3\xc7\x6b\x7d\x59\x67\x1f\x5c\xc6\xee\x06\x80\x53\x31\xa1\x0c\x7f\x4b\x2e\x0d\xba\x45\xa4\x1e\x7d\x76\x3f\x4c\xce\x26\x5f\xc2\xdf\x5f\x46\x1f\xdf\x8f\xce\xbb\xc1\x05\xba\x8d\xfc\xde\x3f\x4e\x13\xfa\x1c\x49\x5d\x76\x42\xc9\x28\xc0\x9e\xa8\x41\xab\xdd\xfd\xb5\xd0\xca\x04\xe3\xa6\x95\xd9\xc2\x64\x21\x7d\xd4\x45\x4b\x1e\xcc\x01\x0c\x94\x6d\xa4\x4e\x64\x94\xd2\x61\xff\xf6\x53\xfb\x03\x3e\xbb\xfd\x76\xfb\xfb\xc9\xb7\x8f\xef\xd1\x79\x97\x7e\x42\x13\x7f\xf7\x2c\x26\x43\xf1\x92\x4c\xd7\xd4\x8f\xd6\x32\xf3\xa3\x45\x13\x3f\x72\xf2\x48\x76\xc7\x39\xb2\x07\x2d\x2c\x39\x3a\x7b\x33\x7b\x75\xf4\xf9\xed\xaf\x9f\xf6\x3f\x8d\x27\xa3\xb7\x47\xe3\xd7\x97\xfc\xa7\xd9\xd9\xc7\x74\xae\xb5\x85\xc5\xe3\xcd\xd8\xd4\x82\x6a\xcc\xf4\x9e\x09\x20\xad\x03\x2e\xfd\xa6\x77\x27\x6f\x9b\x67\xbf\x37\x8f\xfa\xf1\xa5\x14\x72\x0b\xe9\xab\x27\xb2\x36\xe8\xab\x68\xc6\xba\x0f\x46\xb8\xd9\xc1\x5f\xdb\xbb\x01\xf1\x83\xf0\xae\x7d\x37\xf2\x0e\x38\x16\x70\x8f\x07\x9f\x67\x87\xa6\x13\x32\x32\x7e\xcd\x5c\xd2\xa1\x33\xde\xf3\x0f\x0f\xef\xda\x01\xf3\xfc\x59\x6f\x7c\x00\x83\xe1\x01\x0f\x46\x63\xf2\x79\xd7\x9f\x0c\xf9\xe7\xbf\xfc\xbf\xbf\x9e\xfd\x7e\x7d\x79\x0c\xfe\x4b\xcf\xb8\xa5\x30\x7e\x81\x7d\x44\x84\x5c\x33\x33\x04\x80\x39\xd8\xee\xb5\x7b\xdb\xcf\x15\x2d\xd4\x9f\x27\x6f\x3e\x5c\x5d\x9f\x5d\x5e\x69\x62\xc8\x8f\x2a\x95\x9d\x2e\x2c\xc8\x00\xa9\xf6\x9d\xf1\x1e\x65\x7b\xed\x19\x9e\xb6\x0f\x28\x92\xcb\x36\x61\xb7\x5e\x77\xdf\x1f\x8f\xc4\xe7\x0e\xf4\xb6\x4d\x25\x1b\x67\x87\x55\xaf\xca\x49\x18\xf2\xf6\x6f\x15\xf2\xe4\x9a\x7f\x64\xf3\x7d\xc2\xef\x86\x5d\x7e\x11\xbe\xfa\xbc\x37\xfc\x3d\x3a\x3d\x38\x81\x8d\xad\xff\x0b\x00\x00\xff\xff\x5c\xf2\xe8\xad\xda\xd8\x00\x00") +var _connector_mgmtYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x7b\x73\xdb\xb8\xb5\xf8\xff\xfe\x14\xf8\x29\xbf\x8e\xdb\x7b\x23\x59\x92\xe5\x97\xe6\xa6\x33\x4e\xec\x64\xbd\x9b\x38\x59\xdb\xd9\x6c\xda\xe9\xc8\x10\x09\x49\x88\x49\x80\x06\x40\xc5\x4a\x7b\xbf\xfb\x1d\x00\x7c\x80\x24\x48\x51\xb2\x62\x3b\xbb\xe4\x4c\xbb\x31\x89\xc7\x39\x07\x07\xe7\x0d\x88\x06\x88\xc0\x00\x0f\xc1\x6e\xa7\xdb\xe9\x82\x67\x80\x20\xe4\x02\x31\xc3\x1c\x40\x0e\x26\x98\x71\x01\x3c\x4c\x10\x10\x14\x40\xcf\xa3\x5f\x01\xa7\x3e\x02\x67\x27\xa7\x5c\xbe\xba\x21\xf4\xab\x6e\x2d\x3b\x10\x10\x0d\x07\x5c\xea\x84\x3e\x22\xa2\xb3\xf5\x0c\x1c\x7b\x1e\x40\xc4\x0d\x28\x26\x82\x03\x17\x4d\x30\x41\x2e\x98\x21\x86\xc0\x57\xec\x79\x60\x8c\x80\x8b\xb9\x43\xe7\x88\xc1\xb1\x87\xc0\x78\x21\x67\x02\x21\x47\x8c\x77\xc0\xd9\x04\x08\xd5\x56\x4e\x10\x41\x47\xc1\x0d\x42\x81\x86\x24\x19\x79\xeb\x19\x68\x05\x0c\xcf\xa1\x40\xad\xe7\x00\xba\x12\x0b\xe4\xcb\xc6\x62\x86\x40\xcb\xa1\x84\x20\x47\x50\x36\xf2\xa7\xbe\x68\x47\x2d\x3b\x0b\xe8\x7b\x2d\x30\xc1\x1e\xda\xc2\x64\x42\x87\x5b\x00\x08\x2c\x3c\x34\x04\xaf\xe2\x0e\xe0\x12\xb1\x39\x76\x10\x78\xed\x21\x24\xc0\x3b\x48\xe0\x14\xb1\x2d\x00\xe6\x88\x71\x4c\xc9\x10\x74\x3b\xbd\x4e\x77\x0b\x00\x17\x71\x87\xe1\x40\xa8\x97\x4b\xfa\x6b\x7c\x2e\x10\x17\xe0\xf8\xc3\x99\x04\xd3\x57\x1f\x40\x02\x28\xef\x6c\x71\xc4\xe4\x24\x12\xaa\x36\x08\x99\x37\x04\x33\x21\x02\x3e\xdc\xd9\x81\x01\xee\x48\x62\xf3\x19\x9e\x88\x8e\x43\xfd\x2d\x00\x72\x00\xbc\x83\x98\x80\xbf\x06\x8c\xba\xa1\x23\xdf\xfc\x0d\xe8\xe1\xec\x83\x71\x01\xa7\x68\xd9\x90\x97\x02\x4e\x31\x99\x5a\x07\x1a\xee\xec\x78\xd4\x81\xde\x8c\x72\x31\x3c\xec\x76\xbb\xc5\xee\xc9\xf7\xb4\xe7\x4e\xb1\x95\x13\x32\x86\x88\x00\x2e\xf5\x21\x26\x5b\x02\x4e\x23\x02\x10\xe8\x67\xd6\xe5\x6a\x11\x20\x5e\xec\xdf\x6a\xd9\x5a\xd7\x6e\x08\x5e\x79\x21\x17\x68\x85\x0e\xd1\xfa\x5a\xdb\x6f\x05\x50\xcc\x14\xfc\xcf\xe4\xff\x80\xb5\xdb\xb3\xad\x2d\x00\x5a\x72\x19\x76\xb2\x6c\xba\x33\xef\xb5\x86\x6a\xdc\x29\x12\xfa\x1f\x00\xc4\x04\xd1\x4f\xbb\x04\x10\x20\xf7\x22\x83\x12\x90\x33\x77\x28\xfb\xff\xa6\xd9\xf5\x1d\x12\xd0\x85\x02\x46\xad\x78\xe8\xfb\x90\x2d\x86\xe0\x02\x89\x90\x11\xae\x76\x4b\xc4\xd9\xc0\xcf\xb6\xcd\x20\x57\xa3\x3d\x43\x3c\xa0\x84\x23\x03\xdc\x56\xbf\xdb\x6d\xa5\x7f\x02\xc9\xee\x02\x11\x61\xbe\x02\x00\x06\x81\x87\x1d\x05\xfc\xce\x17\x4e\x49\xf6\x2b\x00\xdc\x99\x21\x1f\xe6\xdf\x02\xf0\xff\x19\x9a\x0c\xc1\xf6\xb3\x1d\x87\xfa\x01\x25\x88\x08\xbe\xa3\xdb\xf2\x9d\x1c\xfa\xdb\x46\xe7\x0c\x5e\xbf\xe5\x71\x49\xd6\xae\xc8\x79\x55\x0b\xb7\x73\x03\x27\x37\x70\x94\xbe\x17\xb2\xd3\xce\xbf\xb3\x2f\x46\xd8\xfd\xdf\x88\x1e\x01\x64\xd0\x47\x22\xda\xef\x7a\x6d\x35\xab\x15\xba\x6c\x59\x21\xbf\x9a\x21\x80\x5d\x40\x95\xc4\x4c\x3b\x01\xd9\x69\xab\x9c\x74\xf2\xf3\x10\x70\xc1\x30\x99\x26\xaf\x31\x19\x02\xc9\xba\xc9\x0b\x86\x6e\x43\xcc\x90\x3b\x04\x82\x85\xa8\x3e\x4f\xa6\x9b\x14\x00\x8e\x9c\x90\x61\xb1\x30\x5b\xbe\x44\x90\x21\x36\x04\xff\x04\xff\x2a\xe1\xdb\x64\x2c\x39\xd4\xcb\xc5\xd9\x49\x9e\x73\xdf\x20\x01\x60\x0e\x5f\xa9\x45\x12\x3a\x65\xa8\xb4\xb4\xf5\x23\x71\x6d\xcb\xca\xb5\x19\xe4\x5b\xb9\xae\xe8\x0e\xfa\x81\x67\x02\x1a\x3f\x99\x6e\xa7\xba\x59\xb1\x95\x7d\xea\x78\xd4\x1d\xdb\x20\xad\xb2\x6d\x73\x55\x60\x39\xe0\x43\xe1\xcc\xa4\xba\x90\xec\x28\xf9\x07\x29\xc9\x1f\x91\x74\xd0\xed\x3d\x0e\x49\x4f\x19\xa3\xac\x3e\x29\x07\xdd\xde\xba\x04\x4c\xbb\x96\x92\xed\x38\x14\x33\x20\xe8\x0d\x22\xd2\x20\xc0\x64\x0e\x3d\x63\x7b\xb7\x06\xdd\xc1\x0f\x42\xa4\xc1\xfa\x44\x1a\x2c\x23\xd2\x39\x4d\x79\x29\xc7\x63\xe8\x0e\x73\xc1\x53\x82\xed\x3d\xd6\x46\x5d\x91\x60\x7b\xdd\xee\xba\x04\x4b\xbb\x96\x12\xec\x23\x41\x77\x01\x72\x04\x72\x01\x92\x70\x01\xea\x28\xab\xca\x5d\x59\x5f\xad\x62\x7e\x6c\x58\xd4\xf3\x32\x0b\x05\x02\x0f\x73\x21\xf5\x5c\x96\x19\x78\x95\x99\xb2\xac\x53\x51\xfb\x4a\x90\x6d\x0b\x91\xb6\xdc\x09\xe0\xd4\x58\x84\xa5\xcd\x39\xfe\xb6\x4a\x73\xca\x5c\xc4\x5e\x2e\x56\x99\x00\x41\xe6\xcc\x5a\x4f\x5e\x91\xbd\xc5\x5c\x94\x8b\xc4\x25\x2b\xd5\xe8\x8e\x7a\xba\xa3\x11\x85\x4b\x45\x61\xce\xae\x5f\xd1\xa2\x8f\x85\x63\x20\x3d\xde\x65\xd2\xf1\x1e\x82\xd1\x61\x08\x0a\x64\x42\x09\x4c\xb1\xf8\x4a\x7d\x56\xc1\x91\xaf\xe9\x96\xb1\xc9\xc2\xca\x96\x76\x01\x28\xfd\x80\xdb\x10\xb1\x85\x41\x5f\xed\x94\x40\xbe\x20\x4e\x19\xd5\x3f\x20\x36\xa1\xcc\x57\x96\x1f\x54\xd1\x07\x80\x09\x80\x44\xf7\x9a\x31\x4a\x68\xc8\x81\x0f\x09\x41\x6c\xab\x9a\xdb\xb4\x7b\x32\xa6\xd4\x43\x90\x18\x5f\x2c\x0e\x09\x88\xad\xcc\x97\xd4\x35\x08\x5c\x12\x96\x31\x1c\x55\xeb\xe6\xa8\xde\x1a\xf6\x8d\x51\x4b\x02\x5e\x68\x20\xb3\x3b\xa4\x6c\x7f\x24\xbd\xf4\xe2\x95\xee\x94\x7a\x96\x7c\x66\x90\x56\x95\x73\x57\xa6\x3e\xfa\x8f\xac\x3e\xca\xa5\xa1\xe3\xa0\x40\xa0\x8c\xf1\xfc\x63\x08\xc0\x41\xb7\xab\xd6\x05\x53\xb2\xbe\xb6\xc8\x0f\x51\x4a\xa7\xdf\xa4\x96\x50\x2d\xb5\x40\xe4\xa9\x44\x34\x28\xd7\xe8\xd7\xc6\x37\xab\xe5\x9b\x5d\xa5\xbe\x3d\x72\xa5\xcc\xa0\x21\x73\x10\x70\x29\xe2\x64\x5b\x68\xff\xac\xb1\x49\x72\x8c\x45\x40\x58\x66\x96\x68\x6d\x1f\x47\x4d\xb2\x4a\xba\x8e\x17\x76\x0f\x3b\x43\x9a\xdd\xc5\x71\xfe\x60\xde\xd7\x93\xf6\x8d\x56\xf5\x8b\x1a\x97\xa8\x71\x89\x1e\x27\x3a\xc4\x77\xfe\x5d\x9d\xb9\x58\xb2\x19\xb1\xdb\x7a\x08\x91\x66\xc6\x94\xf2\x02\x2d\x97\x08\xb0\x89\x2f\x7b\x93\xa7\x29\x3b\x6a\x46\xe6\x9b\xa0\x7c\x63\xf8\xd5\x21\x52\x13\x94\x5f\x89\x60\xf7\x12\xbb\xba\xa9\x87\x04\xfa\x9e\xb2\x50\xcf\x50\x2a\x0e\x4f\xd4\xe7\x65\x12\xb1\xb4\x95\x5d\x28\x3e\x95\x8d\x62\xc1\xa1\x71\x77\xff\xb0\x52\x4f\x2f\xf0\x3d\x64\x5f\x66\x80\x2a\x09\xa8\xac\xa2\x58\x8d\x82\xaf\x58\xcc\x00\x0f\x90\x83\x27\x18\xb9\xe0\xec\xe4\x47\x96\x84\xf7\x23\x62\x7e\x80\x35\xa5\x62\x20\x35\xcc\xf7\x14\x8a\x6a\x82\x52\x99\xf8\x41\x7e\x5d\x26\x12\xcb\x1a\x2d\x8f\x45\x9f\x40\x01\x81\xa0\x1a\x88\x5c\xd1\x8e\xe4\xa5\xad\x0a\x36\x31\x99\xc4\x47\x6c\x8a\xda\x6a\x94\xff\xae\x1b\xa9\xd6\x61\x75\x3a\xfe\x82\x1c\x51\x32\xac\x1c\x6a\xc5\x51\x73\x0e\xeb\xcf\x97\xef\xcf\x35\x7d\x9e\x83\x8b\xd7\xaf\xc0\xfe\x51\xb7\x0f\xda\x49\xdd\xa1\xa0\xd4\xe3\x1d\x8c\xc4\xa4\x43\xd9\x74\x67\x26\x7c\x6f\x87\x4d\x1c\xd9\x6a\x3d\x68\x37\x1f\xa2\x4f\x3a\xff\x11\x42\xe4\x8d\x27\xf0\xe7\xd5\x89\x8d\x27\xf0\x23\x78\x02\x96\x5a\xd3\xa8\x1c\x79\xd5\x6a\x53\x27\xaa\x62\x5e\x25\x47\x9d\x2d\x7d\xae\xce\x42\xa7\x60\x81\xda\x9a\x77\x49\xca\x1a\x38\x99\x31\x6b\xa4\xae\x73\x3d\xfe\x74\x29\xec\x08\xfd\xc7\x4b\x65\x47\x5c\xb0\x66\x46\x5b\x77\xde\x4c\x62\xdb\x32\xd6\x0f\x99\xdf\x8e\x10\x69\xd2\xdc\x4d\x9a\xdb\xa0\x5c\x63\xe3\xd4\x20\x52\x93\xe6\x7e\x5a\x66\xce\x1a\x69\xee\x8c\x42\xaf\x55\x74\x9c\x33\x59\xee\x9b\xf6\xce\x0f\x57\x27\xfb\xed\x64\xfb\xd4\x4e\x80\xe7\xfa\x3d\x74\x05\xf2\xd3\x4c\x63\x45\x0b\xb0\x72\x85\x70\x8e\x98\x8d\x74\x6f\x32\xe2\x0f\x7c\x5e\x22\xe6\x40\xf3\x88\x5f\xf4\x6e\xc5\x53\x7e\x69\x2f\xbb\x03\x50\x76\xd0\x2f\xeb\x0d\x3d\xfc\x59\xbf\xfb\xcb\x62\x33\x5f\x9f\xf3\x30\xcb\x4e\xfb\x55\x38\x8d\xd5\x4d\x9f\xb4\xfc\xab\x19\xc3\x8b\x1d\xc0\x26\x96\xd7\xd8\xb9\x75\x88\xb4\x66\x2c\x2f\x66\xb3\x26\xa6\xb7\x6e\x1e\x2b\x7c\x10\xf1\x19\x06\xae\x25\x46\xf7\x72\x71\xe6\xe6\xa5\x68\xe8\x06\x30\x9b\xc7\xaf\x12\xa4\x4b\x5b\xd7\xcf\x75\x69\x10\xdd\x35\x33\x5d\x0f\x12\xbc\x5a\x21\x5a\x94\x15\x19\xd9\x28\x5d\xb4\x67\xb8\x80\x22\x54\xf7\xa3\x44\xa8\x37\x72\xb9\x91\xcb\x1b\x96\xcb\x8d\x48\xb6\xd1\x6c\x23\x05\x57\x1b\x90\xca\xb9\xc2\xab\x12\xbb\xb6\x58\x59\x55\x25\x91\x97\xb6\x6e\xea\xb1\x1a\xb9\xf8\x44\x88\xf4\x80\xf5\x58\x49\x60\xb6\x29\xc5\xda\x64\x29\xd6\xe6\xa2\x20\x3b\xd0\x75\x29\x19\xa5\x51\x90\x26\x2c\xb2\x5e\x58\xe4\x58\xd2\xf1\x43\x42\xb5\xbc\x36\x29\x09\x7d\x6c\x73\xa0\x16\xc0\xa0\xb7\x4d\xbb\xac\xdc\xfb\x49\xc5\x52\xb2\xa4\xa9\x8c\x24\x4b\x96\x49\x91\x01\x62\x06\x05\xe0\x33\x1a\x7a\x2e\x18\x23\x10\x72\x7d\xdb\xa0\x43\xc9\x04\x4f\x43\x86\x14\x63\xe9\x7b\xfa\x4c\x0f\x46\x13\x85\x12\xcd\x77\x9a\x56\x9d\x46\x9d\xfd\x51\xd5\x59\x13\x7e\xf9\x91\x6c\xfd\x0d\xea\x2e\xa9\x90\x78\x00\x1d\xf4\x83\x6b\xad\x15\xf3\x8a\x2b\x65\x15\x57\xbd\xd5\x68\xa5\x3b\x8d\x1e\x4f\xdd\x9e\x27\x4b\x5f\x5f\xd3\x92\x7c\x9f\x9a\x3a\xb6\xd0\xef\x49\x69\xd7\x84\x32\x09\x49\x96\x6a\xd8\x14\x21\x30\xc7\x1c\x8f\x3d\x75\x9d\x70\xc8\x11\x03\xb8\x51\x9a\x8d\xd2\x6c\x94\xe6\x13\x54\x9a\xf9\x3a\xe4\x8c\x04\x5c\xa9\x14\xb9\xa8\x36\x6b\x15\x23\x17\x44\x6e\x55\x39\x72\xd2\x78\x25\xa9\xbf\xac\x20\x99\xe4\x46\xad\x53\x92\x9c\xef\xb3\x4a\x3d\x6f\xd2\xf7\xf1\x2a\x7a\x13\x42\xae\x57\xd3\x9b\x74\xdf\x48\x55\xaf\x7d\xb4\x1f\xb2\xae\x37\x41\xa5\xa9\xec\x6d\x2a\x7b\x0d\xca\x35\xd6\x43\x0d\x22\x35\x95\xbd\x4f\xcb\x70\x58\xa7\xb2\x37\xab\x17\x6b\x79\x72\x45\xa7\xeb\x9e\xd5\xbd\xe5\x5e\x5c\x55\x9d\x6e\xb5\x1f\xb7\x52\xcf\xe6\x96\xe1\xf4\x79\x02\xce\xa9\xad\x90\xb8\xb0\x66\x8d\x3a\x69\x4a\x89\x1f\x38\x10\x99\xf2\xa0\x19\x8a\x4c\xde\xae\x58\x4e\x6c\xf6\xb3\x7b\x20\x65\x31\xc8\xbc\x2f\xf3\xf0\xb9\xb3\x4d\xa8\x00\x33\x9c\x57\xf0\x13\xcb\xc2\x78\x95\xae\xdf\xb2\xc6\x4f\x5c\x26\xd6\x2c\x2e\x4e\xbd\xd1\xa6\xbc\xb8\x31\xb6\xeb\x10\x69\xcd\x50\x5d\xca\x68\x4d\xb0\xee\xbb\x5e\x94\xb3\x11\x71\x9a\x2b\x32\x4e\x86\xac\x59\x66\x5c\x29\x58\x6b\xb4\x5f\xb5\xd4\xd8\xe0\xae\x47\xab\x34\x4e\x68\xa4\xee\xc2\xf9\x4e\x05\xc7\xc9\x24\x4d\xc9\x71\x23\xab\x1f\x40\x56\x37\x62\xda\x46\xb5\xcd\x14\x1d\x6f\x42\x4e\xe7\xca\x8e\x4b\x2d\x5f\x4b\x29\x71\xa5\x8c\xae\xd1\xbe\x29\x3e\x6e\x24\xe4\x13\x21\x52\x53\x7c\xfc\x27\x2a\x3e\x36\xe2\x26\x68\x0e\xbd\x8d\xa7\x9b\x4f\xe7\xd0\x0b\xd5\xbb\x4d\xe6\x9b\xf9\x8c\x32\x01\x3c\x3c\x97\xb8\x27\x33\xac\x95\x86\xae\xd5\xfd\x47\xcd\x48\x4b\xea\xdf\x33\x2b\x2d\x87\xd8\x6c\x66\xba\x30\x62\x93\x9d\xfe\xde\x50\x37\xd9\xe9\x07\xa3\x5c\x63\x62\xd4\x20\x52\x93\x9d\x7e\x5a\x2e\xd8\xfd\xb2\xd3\x5b\xe9\xac\x12\xb8\x08\xc3\xa1\xbe\x09\xf8\x99\xfe\x7f\xf0\x8a\xfa\x3e\x25\xd1\x2b\xf5\x9f\xb7\x38\x35\x32\x12\xc1\x6f\x18\x03\x37\x98\xb8\xc6\x9f\x01\x9c\x22\xe3\x4f\x8e\xbf\x99\x7f\x0a\x2a\xa0\x67\xfc\x8d\x05\xf2\x63\xb3\xc4\x72\x15\x72\xc0\xa4\xad\x22\xb0\x49\x6a\x39\xdf\xd2\x34\x8d\x84\xa2\xd8\x08\x13\x81\xa6\x66\xe5\x39\xfe\x56\xa3\x95\x82\xb9\xbc\x99\xfa\xa0\xd8\x24\x6e\x03\x3d\xef\xfd\xc4\x24\x51\x15\x83\xbd\x57\xf8\x5e\xa0\x09\x62\x88\x38\x99\xfc\x76\xc9\xdd\xd0\x36\xa2\x00\xb5\x27\xdc\x02\xd7\x59\x89\x03\xd4\x4a\x42\xcb\x0e\x29\x6d\x9e\x98\x8c\x23\xec\x56\x76\x52\xdf\x72\x38\x0d\x57\x5b\x60\xbc\x7c\x79\x6b\xf1\xc0\x4c\x52\xbd\xac\x91\x01\xe7\x3b\x24\xe0\x8a\x20\xd2\xaf\x04\xb1\xa5\x00\x68\xd3\xda\x1d\xc1\x8c\x9c\x9a\x50\xe6\x43\x31\x94\x66\x27\x6a\x0b\xec\xa3\x65\xc3\xf8\xd4\x55\xee\xd6\xba\xe3\xa8\xf7\x97\x88\xcd\xb1\x13\x07\x4d\x30\x25\x97\x48\x48\x69\xc1\xab\xb6\x36\x36\x37\x76\xc8\xbc\xfb\x2d\x5a\xc8\x2c\xbb\xc8\x02\xe3\xb1\xe3\xd0\x90\x54\xca\x1c\xc7\xc3\x88\x88\x51\x06\xbe\xe8\x1d\x47\x0e\x43\x55\x6b\x97\xf4\x5d\xbe\x7e\xe6\x88\xd5\xa0\xff\x86\x18\xc7\x94\x48\x56\x92\xee\xc4\x03\x49\x02\x64\x53\x35\x6a\x6f\x80\xd6\xf1\x87\xb3\x08\xa8\xac\xf6\xc2\xf2\xe3\xbc\x97\x7d\x39\xd3\x60\xd9\x9d\xd1\x56\x4e\xca\x78\x9e\xe6\xa0\x82\xfa\x6b\xeb\xc1\x95\xef\xca\xf3\x3a\x73\xc9\x24\xc5\x9f\x20\x2e\xf4\x8f\x10\x2b\xfd\x4d\xb9\x72\xb9\x58\x0a\xb1\xa6\x2b\x64\x0c\x2e\x72\x5f\x94\x62\x2a\xea\xf0\xdc\x82\x9a\xb8\xaf\xb4\xb4\x19\x9d\x1b\xf1\x3d\x37\xb5\xee\x2f\x92\x1c\xe5\xbb\x35\x63\x16\xfc\x44\x3d\x97\xc7\x6a\x5f\x9d\xe7\xd4\x66\xba\x3e\xe0\x29\x47\x90\xff\x84\x7a\x4c\x70\x46\xb8\x80\xc4\x41\x9d\x75\x78\xb4\x54\x8c\xa4\x0b\xf1\x2c\xfa\xed\x90\x28\x4c\xe4\x18\xeb\x92\xb6\x29\x61\xe9\x67\xd9\x55\xd4\x52\x41\x4d\x7d\x81\xa6\x98\x0b\xb6\xd8\x30\x49\xd4\xe0\x20\x1e\xfc\x01\x68\xa3\x1b\x03\x16\xcf\xb8\x29\x2a\xc5\xbc\xa4\x8e\x08\x67\x38\x29\x7b\x68\xd8\x4a\xad\xd6\x71\xfe\xf8\x73\x6b\xe3\x2a\x7b\x0e\xbd\xd0\x62\x6c\x99\x42\xb4\x78\xbc\xb9\x0c\xda\xb8\xb8\x2d\x7f\x68\x3b\x0b\xb6\xb9\xaf\x73\xfb\xb9\xfe\x29\xeb\x56\xde\x3e\x2e\x5e\x5f\x9f\x90\x3a\x7f\xb6\xee\x52\x40\x91\xb3\x7e\x32\x54\x41\x24\xf4\x4d\xee\x72\x31\x8f\xb8\x13\x99\x9a\x8d\x21\xe8\x2e\xec\x33\x44\x51\x23\xd3\x84\xb1\xad\x8f\x2a\x9c\xaa\xa4\x7d\xc9\xc0\xf6\x05\xd0\x5b\x52\x5a\x20\x66\xdd\x8c\x91\x99\x86\x2a\xa8\x06\x02\x0f\x12\x64\x9c\xff\xd3\xe9\xdb\xd6\x3a\x9b\xab\x02\xf1\x96\x1d\x03\x93\x26\x6b\xe8\x61\x3d\xf2\xf7\x02\xee\x52\x51\xa2\x6a\xc9\x78\xa6\x45\xc9\x5e\x2c\xeb\x1c\x0f\x50\xf0\x07\x56\xc1\x42\x71\x6f\x2e\x1e\x69\xba\x39\xf5\x99\x69\xd3\xe6\xd0\x2a\x58\xdc\x67\x1d\x2f\x23\x7e\xb5\x22\x65\xca\xa7\x95\x10\xcb\xda\x2d\x2b\xbb\x79\x56\xcb\x64\x65\x43\x66\xb5\x2b\x3b\xed\x22\xd0\x78\xfb\x6a\x06\x09\x41\x5e\x85\xac\x73\xd1\x04\x86\x9e\x90\x6f\xe1\xd8\x43\x25\x12\x30\xfa\x98\x25\xf8\x09\xe2\xd2\x01\x58\x55\x9a\x6a\xb1\x69\x8e\x4d\x83\x20\x23\x58\xdd\x28\x45\x9a\x9d\x6e\xd5\x79\x20\xe7\x78\x4a\x4c\x5d\x17\xbf\xcb\x4c\xa6\x44\x63\xb6\xd5\x72\x08\x27\x10\x7b\x45\x90\xb3\xa3\xb8\xb9\x44\x6f\x5b\xb2\xce\x1c\x4b\xd3\x3f\xdf\x30\xf3\x21\xc7\xd5\xa6\x9d\x54\x19\xef\x91\xd6\x9d\x09\xb4\x36\x7b\x46\x50\xbb\x6d\xc6\x97\xfc\xef\x86\x59\xa3\x39\x72\x34\x93\x3d\xab\x18\xb3\xc4\x28\x4e\x37\x53\x0e\x96\xe2\xb8\xdb\x55\x96\x5b\xe4\x78\x6e\xa7\xc3\xa9\xef\xa3\xd8\x58\xab\x0b\xe6\x32\x8b\x35\x85\x37\xa1\x90\x39\xf4\x33\x23\x7a\x57\x65\x1e\xca\x96\x4a\xcd\xf2\x19\x0c\x50\xe6\x75\xc0\xa8\x83\x38\x37\x7f\x47\x5b\xbe\xd6\x11\xc3\x19\x24\xae\x97\x0d\xf0\x64\x44\x50\x96\x2f\x2c\x16\x86\x8d\x2b\xa4\x85\x61\x5b\xfa\x91\x1c\x3a\xeb\xa8\xbb\x7a\x3b\x8f\x94\x82\x5a\xd7\x68\x29\x50\x30\x9e\x68\x69\x0f\xb3\x68\x7c\xf9\xf0\x59\xb9\xb6\x6c\xe5\x23\x31\x98\x2e\x70\x06\xd7\xda\xa3\xd8\x04\x5f\x5e\x0b\xe5\x0c\xb4\xf5\x8c\xa9\x8c\xa1\xb2\x62\xdf\x8c\xc0\xc8\x43\xf7\x18\xc6\x57\x09\x32\x2b\xaa\xd7\x38\x13\x31\x9a\xeb\xe8\x89\x5d\xd3\xe6\x63\xc4\xfa\x89\x43\x72\x98\x88\xfd\x81\x45\xab\x3c\x59\x8b\x6f\x03\xa6\xde\xa3\xd8\x78\x9b\x60\xdc\x15\x7b\xdb\x6d\xc2\x3f\x81\x31\xd8\xb2\x1b\x81\xe0\x6a\x11\x64\x43\x57\xc9\x27\xf9\xc5\xea\x40\xfe\xbd\x9d\x80\x70\x81\x02\x86\xb8\x9c\x31\x53\x77\xa7\xca\xe5\x79\x18\x04\x94\x09\xe4\x82\xf1\x42\x39\x9a\xc7\x1f\xce\xa2\x8e\x94\xa0\x2c\x8d\x8b\x3a\x09\x14\xf5\x92\x7e\x15\x6d\xec\xdc\x5b\x8d\xef\x26\x47\xfc\xc2\x29\x19\x65\x86\x7d\xa4\xc4\x50\x5e\x91\x16\xd6\xe3\x1c\xfa\xa8\x78\x46\x4a\xce\xd2\xa9\x12\x00\xe6\x87\x12\x69\x99\xad\x20\xd0\x6d\xee\x39\x53\xa4\x92\x0b\x6c\x9c\xad\xf3\x89\x1a\xad\x32\xd7\xa6\x36\x4c\xde\x06\xc8\x03\x57\x05\xf7\xb1\xf9\x67\x01\xf8\xda\x34\xc2\x0e\x25\xa3\x7c\xfe\xab\x30\xd9\xc7\x8b\xb7\x2a\x0a\x4a\x54\xfb\xf5\x67\xf3\xe0\x78\xd9\x7a\xbc\x55\x4d\xd2\x7b\x07\xa1\x40\x53\xca\xf0\x37\x94\x9d\xf2\xbe\xeb\x52\xce\x34\x30\x80\x63\xec\xe1\xe2\xe6\xb0\x1d\x14\x33\x1a\x17\x85\x90\x23\xd7\xfb\xbb\x02\x6b\xaf\x53\x28\x93\xa0\xf1\x73\xac\x04\x4e\x1c\x60\x56\x17\x3e\x3a\x90\x98\xb7\x3d\xce\x75\x05\x0f\x02\xb0\x60\x46\x16\x46\x4b\x37\xcc\x04\x23\xcf\xb5\xf3\x42\x41\x02\x01\x53\xe8\xfd\x38\x08\x14\xd5\xd6\x9f\x40\x9f\x4b\x34\x4b\x83\xdb\xb9\x9a\xd1\x67\x59\x0a\xe5\xaf\xd2\x59\x23\x83\x5e\xcb\xb9\x83\x84\x50\x01\x0b\x99\x3b\x3b\x3d\x2c\xb4\x28\xe5\xd2\x32\xf2\xdb\x75\x65\xc5\x56\xb5\xe4\x35\x96\xf4\xb0\x9b\x15\x56\xc3\x42\x99\x16\x72\xf8\xad\x12\xf2\x3f\x86\x97\x65\x5b\xfb\xbc\x35\x9c\xb4\xb9\x42\x04\x12\xf1\x8b\x51\xad\x51\x23\x9e\x16\x72\xc3\xb1\x6a\x03\xca\xa6\x90\x60\xae\xd8\xa0\x7a\x9e\x0a\x16\xac\x51\xb8\x94\xc4\x28\xea\x14\x1d\xad\x46\xaa\x94\x0c\x29\xb9\xb3\xc1\x87\x8c\x6c\x3c\xc5\x62\x86\x98\xbe\x46\x90\xb2\x0c\x01\x00\x76\x81\x8b\x02\x44\x5c\x4c\xa6\xf1\xc5\xbc\x8a\x47\xa4\xfa\xce\x60\x54\xe9\xad\xe5\x57\xd1\x6a\xa6\x1f\x5b\x4f\x47\xe9\xaa\x96\xdc\x61\xe9\x3a\x21\xa1\xe2\x75\xa1\x99\x35\x58\x2f\x82\x61\x67\xc7\x74\x90\x55\x6b\xa8\x12\x20\x2b\x5d\x5f\xf3\x43\x9e\x35\xee\xc7\x1e\x25\xcb\x64\x1e\xaf\x5c\x61\xad\xd4\xe1\xd9\xdc\x52\x3d\x00\x9d\x4b\x90\x30\xca\xdc\xed\x38\x90\x25\x65\xfe\x6b\xf1\xde\x26\xf1\x2d\x41\xec\x4f\x60\x2d\x18\x75\xf4\x25\x44\xa8\xcf\x96\x1b\xf6\x86\x57\x83\xff\x9e\x02\xc2\x66\x1f\xac\xea\x14\xd7\x0f\x21\x9a\x5f\xd6\x10\x4d\xe8\x2e\xc0\xd9\x84\x4e\xfc\x3c\xab\xaa\x1e\xac\x1c\x53\x64\x54\x6d\xfc\xac\x23\xf0\x4c\x0f\xb9\x2c\xd8\x82\xdd\xdc\x8b\xda\xd1\x17\x8b\xba\xd1\x1f\x34\x02\x92\x8b\xf3\x57\xcc\xa4\x94\xd5\x17\xcd\x24\x5d\x0b\x1e\xe2\xd9\x89\x54\xb7\x0c\x39\x94\x25\x37\x03\xe4\x7c\x1e\x0b\x01\x73\x77\xc7\x58\x4e\xd1\x98\x65\xcb\x1a\x06\xa3\x9c\x3a\xff\x73\xff\xd9\x1f\xf5\x87\x53\x04\x30\x71\xd1\x5d\x61\xf4\x09\xf4\x38\xaa\x0f\x65\xb1\xb8\x3d\x5f\x4c\xad\x4d\x5e\xd0\x8a\x2a\x03\xcd\x2a\x6a\x0d\xb4\x51\xf4\x5d\x09\xf4\x79\xe8\x8f\xa5\x8d\x33\xd1\xa2\x09\x60\x02\x10\x74\x66\x26\xd2\x1b\x44\x23\x5f\xed\x9d\xa0\xd1\xed\x6a\x44\xa2\x0b\xc3\xac\x82\xec\x3f\xa9\xbf\x7a\x19\x9d\x7f\xd4\xf5\x65\xaa\x13\x18\x2f\x80\xc3\xb0\x40\x0c\xc3\x8e\xe2\x10\xbe\x20\x02\xde\xe9\x98\x0a\xe6\x29\xab\x01\xcc\x0d\x80\x7c\xec\x41\x26\x3d\x5b\x91\xeb\x82\xc0\x75\x3c\xf0\x35\x70\x3c\x18\x72\x65\xe0\x41\x02\x2e\x7f\x7d\xab\x03\xfd\x3e\x22\x22\xf5\x6a\x4f\x25\xdd\x14\xa1\x63\xa7\x59\xf5\xd7\x61\x0b\x48\x16\xc9\xb0\x19\xff\xef\x5a\x3b\xc7\x3c\x1d\xe7\x35\x65\x31\xe9\x9e\x4b\xc0\x98\xba\x04\x4e\xca\x6a\xc3\x3b\x94\xe4\xe6\xe6\x04\x62\x86\xb0\x16\xf0\xcf\xa5\xd9\xaa\x66\x9a\x50\xcf\xa3\x5f\xa5\x99\xaa\x11\x8b\x0a\xd5\xe4\x73\x7d\x7d\xcd\x6f\xbd\x8c\x2f\x08\x20\x77\xcc\xef\x69\xe3\xab\xd5\x81\x00\x23\x48\xdc\x51\x2c\x18\xee\x03\xd2\xf3\x78\x90\x72\xf8\xce\x34\x61\xcd\x15\x26\xdb\x42\xe7\xe0\x5d\xe4\x3e\x97\x26\x3c\x9e\x18\x86\x3a\xe6\x00\xf9\x81\x58\x3c\x97\xef\x52\xb1\xa5\x2b\xa9\x78\xe8\x09\x0e\x20\xcb\xac\x9f\x84\xa6\x93\xf0\x75\xe0\x51\x17\x65\x8e\xde\x15\x79\x3d\xc7\xca\x26\xbb\xc7\xa8\xb5\x4a\x76\xa8\xde\xc2\xd1\x00\xf7\xdd\x85\x5c\x2c\x3c\x34\x54\x5a\x4d\xcb\x0a\x75\xc3\x9e\x7d\x87\xa5\x1b\x4c\x35\x4a\x37\x94\xc1\x0b\xd5\x3b\x6b\xc9\x8e\xfa\x3a\x43\x0c\x65\xb6\x53\x3a\x65\x66\x57\x81\x63\xc9\x27\xc8\x8d\x76\x47\x7c\x97\xbb\x06\x5e\x2d\xce\xb5\xa4\xd2\xf5\x73\x70\x6d\xa0\x20\xff\x8c\xb8\x45\xfe\x53\x45\x45\xaf\x9f\x03\x48\x5c\x70\x1d\x05\xad\xaf\xd3\x8d\x16\x4f\xa1\x4f\x56\x50\xa6\x17\xfd\xfa\x7f\xfe\x2e\xfb\xbe\xb8\x56\x6c\x73\xfd\xf6\xec\x97\x53\x4b\x1f\x87\x92\x2f\x21\x71\x04\x9e\xa3\x7c\xff\xe3\xf3\x93\x6b\x3d\xe5\xfb\x8b\xeb\x0e\xf8\x89\x7e\x45\x73\xc4\x9e\x83\x05\x0d\x95\x60\x90\x98\x43\xe0\xc3\x3b\xec\x87\xbe\xa4\x41\xaf\x9b\x0e\x47\x89\xc2\x15\xc6\x98\x2a\xb6\x30\xc8\x7f\x9a\xf0\x99\x6d\x77\xe6\x72\x42\xfa\xb0\xb8\x88\x6e\xc9\x07\xd7\xf0\x2b\x6f\xf3\x5b\xde\xd6\xe9\x55\x0d\xa4\x8a\xa7\x6a\xd2\x80\x6b\x5d\xfb\x73\x5d\x77\xbb\x66\xf7\xea\x0b\x90\x1d\x5f\x0d\x1f\x0f\xfd\x22\x5b\x74\xa4\xba\xff\x33\x68\xff\xcb\x8e\x86\x2e\x93\xc6\x51\x29\xb0\x46\x03\xea\x59\xf4\xcf\xe8\x08\xc8\x04\xd7\xef\x25\x56\x6b\x42\xec\xe1\x1b\x24\x81\xfe\x4b\x7f\xef\xbb\x08\x16\x25\x2e\xe5\xc7\xec\xb2\x18\xf2\x06\x0a\xf5\x5d\xc5\x19\x66\x90\x83\x00\x31\x1f\x73\x1e\xd5\x49\x73\x84\x14\x4b\x69\xba\x20\xd7\xe0\x83\x73\x2a\x50\x27\x86\x4f\x2b\x9d\xf4\xa0\xa3\xe4\xf8\xa8\xd2\x04\x73\xa3\x77\xb9\xf8\x8a\x8c\x06\xc5\x73\x25\x42\xc9\x2e\x80\x2c\x3a\x3e\x23\x5f\x40\x5e\xec\xd5\xe2\x92\xd6\x7a\xe2\x6d\x2b\x3d\x2b\xaf\x0a\x80\x62\xb0\xa2\xc3\xf2\xe6\xa0\x68\x08\xc6\xea\x6d\xf4\x52\xff\xf1\x3a\x32\xc9\x7f\xfe\x74\xb5\x65\xce\x38\x13\x22\x90\xa3\x67\xb1\xcd\x17\xe9\x59\x0f\x7f\xe7\x82\x93\x9a\xd0\xad\x77\x8b\xcc\x6f\x71\x67\x2c\x82\xea\x01\xb0\x3b\x04\x1e\x9d\x8e\x38\x26\x37\xa3\x6e\xa7\x97\x7c\xd0\x67\x33\x32\x23\x25\xdf\x56\x3a\xf7\xa1\xca\x78\xf8\x8e\x39\x49\x2b\x07\xff\x5b\x3a\x05\x97\x98\xdc\x24\xaf\x63\x37\x0b\xb4\x32\xad\x6d\x89\xc2\x76\x5e\x12\x64\xb3\x54\xf9\x91\xd3\x3c\xda\x9a\xf0\x77\x02\x32\x4d\x21\x2a\x26\xca\xda\x80\x9b\xf3\x95\xa5\xa9\xda\xaa\xd0\x6b\x94\x2f\xf4\x6a\xdb\x0a\xbd\x8a\xc9\x97\xf2\x83\x31\xbe\x5f\x74\x0d\xd3\xad\x96\x5e\xef\x10\x3f\x02\x0b\x4f\xaf\x80\xd5\x5d\xb4\x44\xda\xab\x62\xed\x00\xf8\xa1\x27\xf0\xc8\xc3\xc4\x7a\x5c\x36\x29\x19\x35\xf7\x7c\xb6\x81\xb1\x78\xef\xe4\x58\xe0\x2d\x26\xb6\x96\x11\xe0\xd5\x6d\x14\x0e\x63\x4a\x3d\x04\x89\xe5\xfb\x5d\x7b\xca\x68\x18\x0c\x41\x0b\x11\x37\xa0\x98\x88\xe2\x19\x25\x3e\xa3\x5f\x47\xd0\xf3\xee\x8f\xce\xe5\x8c\x7e\x95\x0a\xbf\x1c\x99\xaa\x16\xf7\x44\x45\xd0\x00\x3b\x4b\x32\xec\xd4\xf7\xa5\xa1\x20\xd5\x93\x40\x6e\x72\x24\x43\x6b\x4f\x35\x80\x8e\xf8\xd8\x59\xe8\xaa\xbc\x41\x79\xae\x25\x05\x5b\xed\xba\x2c\xcc\x5c\xa0\xe0\xfe\xc1\xb0\x5c\x61\x49\xfa\xb4\x2b\x19\x39\x1a\x93\x70\xc4\xc4\x48\x59\x8d\x65\x6d\xca\xfd\xca\xe2\x73\xec\xba\xaa\x2c\x26\xe4\x82\xfa\xda\x18\x8d\xcd\x11\x87\x2a\xfb\x44\x44\xaa\x3f\x32\x78\x7d\xc4\xb9\x0e\x04\x00\xc1\x20\xe1\x58\xe4\xf3\x9e\xe9\xb3\x1c\x1d\xf9\x2c\xc1\xa5\x80\x4f\xfc\xab\x48\xb1\xd1\xad\x81\x16\x54\x7a\xa4\xd0\x75\x91\x5b\x39\x54\xc4\x1c\xaf\x65\xa7\xea\x86\xe5\x4c\x62\x3e\x25\xc9\xb9\x4a\xe8\x93\x6c\x4a\x02\x7e\x1d\x90\x7f\x53\x79\xba\x7b\x83\x5c\x96\x1d\x34\x9f\xf6\x52\xa8\xe2\xb4\xe1\x12\x98\xcf\x14\xbb\x6a\x6a\x83\x63\x65\xff\x97\x77\x29\x17\xf0\xf5\x20\x6f\x67\x76\x87\xb5\xd1\x92\x39\xea\xec\x40\x74\x27\x18\x74\x56\xdb\x82\xa7\xba\x0f\x80\x11\xb3\x4e\x18\xf5\xd5\xe2\x8f\xa9\x9b\x97\x1a\xe9\xf3\xc7\xdf\x3e\x9b\xe0\xc5\x08\xa2\x98\xc4\x0f\xc5\x6a\x19\x36\xf8\x5e\xbc\x36\x83\x7c\x34\x43\xd0\x45\x6c\x34\xc1\x9e\x40\x85\x6a\xd9\xf4\xc9\xac\xf1\x6b\xd5\x18\x8c\x21\x97\xee\xbf\x0e\x2d\xe8\x22\x48\x47\xad\x3b\x25\x08\xe8\x71\xef\xc9\x7c\xf6\x5a\x86\x52\xb8\x24\xef\xe9\x79\x23\x67\x97\xc6\xe9\xb8\x6a\xc1\x16\x9f\x42\x8f\x3a\x9f\x17\xcb\x18\xb2\x4f\xc4\x13\x3f\xe9\xa9\x96\x37\xdf\x1c\xaf\x5a\x2a\x2c\x8a\x60\x41\x1e\x83\x16\x2d\xd4\xf7\x67\xd7\x02\x27\xd5\x63\xd9\xd4\x05\xac\xed\xfb\xbd\x5b\xbc\xa5\x53\x33\xeb\x94\x39\x0d\x01\x5a\x47\x63\x3e\xef\xf2\x03\x41\xd0\xc1\xb4\xdb\x9f\xce\xf6\xa6\x03\xc3\x7f\x29\x1c\xd6\x31\xfa\xec\x8f\xd9\x84\x75\xbb\xfd\x60\x42\x6e\x66\x5d\xd3\x34\x4b\x2f\x5c\x00\x2d\xce\xe6\x4e\x1b\x3a\x8e\x68\xf7\xf6\xfb\x68\xd2\x77\x0f\xdb\xdd\x7e\xf7\xa8\x3d\xe8\xf5\x0e\xda\x87\x83\xfd\x7e\xdb\x9d\xec\xef\x3a\xfd\x6e\x7f\xcf\xe9\xef\x5b\x46\x89\x2e\x63\x00\xad\x71\x6f\x30\x70\x8f\x8e\x7a\xed\xee\x21\x1a\xb7\x07\x83\x83\x7e\xfb\x10\x39\xbd\x36\x1a\x77\x77\x07\xce\xfe\x51\x7f\xb7\x37\x36\xfb\x87\xcc\x1b\x82\xd6\x84\xd2\xb6\x0d\xde\xce\x0d\xe4\x1d\xe8\xf8\xa8\xe3\x50\x7f\x38\x18\xec\xb6\x72\xfe\x94\xf5\x10\x90\x81\x7e\xf7\xe6\xd0\x23\xd3\xee\x6e\x8f\xa3\xa3\xdb\x1a\xe8\xa3\x6e\x7f\xaf\xbf\xbf\x87\xda\xf0\xf0\x10\xb6\x07\x83\xc9\xb8\x7d\x38\xd8\xeb\xb6\x91\xdb\xed\x75\xd1\x78\x7f\xec\xec\x39\x55\xe8\xbb\xce\x1e\x3c\xec\x1f\x1d\xb6\xc7\xc8\x3d\x68\x0f\xfa\x7d\xd4\x3e\x3c\x1a\x1c\xb4\x27\xfb\x13\x17\xee\x1f\xf5\x8f\xfa\x93\x49\x11\xfd\x31\x64\x11\xfa\x7d\x7f\xe2\xc0\x6e\xb7\x2f\x8e\x6e\x0f\xf8\xb4\xc3\x59\x19\xfa\xf1\x09\x98\xbc\xe3\x5c\x3c\x78\x03\x5a\x76\xaf\xdd\x7a\xc8\xc9\xe6\x7b\x26\xce\x93\x19\x1c\xd2\x4f\xea\x28\xf2\xc2\xd7\xc8\x59\x51\x8b\xfb\x7c\x0c\xb3\x37\xa2\x26\x6e\x73\x76\xaa\x1b\xb4\xc8\x6f\xc7\x38\x23\xda\xba\xbc\xba\x38\x3b\x7f\x93\x75\x2e\xac\x86\x64\xd2\xe3\xe7\xcb\xf7\xe7\xb9\x9b\x28\x22\xaf\xbc\x90\xcf\xac\xf4\x10\xa2\xf8\x8c\xfa\x2a\xe5\x62\xd1\xbf\x8c\xa3\x59\xaa\x89\xb2\x39\xcb\xce\x1a\xe5\x0e\x17\xaa\x80\xdc\x28\x3e\xfa\x95\x2d\x3d\x82\xee\xc8\x43\x42\x20\x36\xba\x0d\x51\x1e\x4d\x45\x5d\xc9\x70\xde\x6d\x2e\x5c\x54\xfd\x8b\x78\x65\xa1\x27\xcb\x85\x7c\x46\x66\x7b\x99\x04\x2a\xa9\x08\x8c\x7f\x1a\xa4\x95\x8d\xcf\x74\x60\x80\x3b\x34\x40\x84\xcf\xf0\x44\x48\xde\xde\x09\x18\x9d\x60\x0f\xd9\x56\x17\xb4\x22\x0f\xbd\x9d\x69\xb4\xc2\x5d\x8b\x65\x38\xcb\x0e\x16\xbc\x1f\x01\x99\xf2\x2b\x03\x2d\x91\xbe\x56\xaf\x6b\x08\x82\xe8\xfa\x95\xdc\x85\x68\xd5\xc1\x31\x7d\x51\xe0\x4e\x66\x1c\x75\x4d\x15\x68\xbd\x7a\x7f\x7e\x7e\xfa\xea\xea\xfd\x45\xfb\xdd\x9b\x77\x57\xed\x4c\x93\xe8\x72\x2a\xd0\xba\x5c\x10\x67\xc6\x28\xa1\x21\x07\xd0\xd1\x05\x70\x1c\x10\x2a\xd2\xb2\x6c\x1d\x7c\x87\x7c\x41\x9c\x17\x52\x30\x14\xef\xb0\xc8\xdd\x5e\x05\x5a\x3d\xfc\xe9\x0c\xfb\xb7\x6f\x1c\x76\x12\xbe\xdd\xef\xc1\x8f\x77\x67\xff\xb8\x7d\x79\x75\x7b\x7e\x01\x13\x2a\x9d\xe9\x60\xf6\xaf\x21\x62\x8b\x1a\x94\xea\x6f\x88\x52\xfd\xa5\x84\xea\x5b\xe8\xf4\x1f\x83\x01\x5e\xab\x13\xc3\xd2\x78\x0b\x20\xe3\x28\x93\xca\x19\x82\x8f\x04\x46\x3f\x52\xac\xe2\x35\x3a\x58\x13\x95\x53\x71\x75\xa1\x03\x0c\xf0\x48\xc7\x34\xa3\xc3\xb4\x43\x50\x80\x60\xb8\xc2\x7c\x69\xfd\xbc\x43\xbd\xd0\x27\xda\xb6\x94\x33\x45\xb1\x7a\xb0\x8d\xdd\xed\x0e\xb8\xb4\xb5\x53\x49\x2d\x73\x36\xa9\x85\x29\x79\x1e\xa5\x9a\x1d\x8f\x86\xee\x28\x4a\x88\xb0\xf8\xad\x3e\x47\xd7\x01\xbf\xea\xc4\x84\x5e\xc8\x21\xc0\x2e\x78\x01\x7a\xfd\xdd\x52\xae\xf0\x3e\x9d\xbc\x09\x17\xe3\x33\x76\x4a\xee\xd8\x31\xf2\x0f\xfa\x83\xe9\xed\xcd\x0d\x3e\x99\xc7\x5c\x91\xbf\x0f\xd1\xc6\x09\x83\xee\x60\x23\x9c\x70\xb0\x8c\x11\x0e\x2c\xfb\xa5\xce\xa5\x8a\x09\x32\xd6\xfb\x8f\x6d\x28\x1d\x3c\x1e\x42\x69\xee\x4a\xc5\xbd\xb0\xfb\x62\xbb\x87\x7f\xd9\x75\xc3\xdf\x3e\x9f\xcd\xe7\x7b\x9f\xe7\x6f\xbd\xc5\xb7\x9e\xff\xe6\x62\xf7\xe7\xc5\xed\xf9\xb6\x12\x0d\x13\x1a\x9a\x85\xb6\x85\xcd\xff\xf9\xfd\xc1\xb4\x3f\xdd\xff\xe9\xca\xfd\xf8\xcb\x47\xd8\xbf\xe1\x3f\x1d\xf6\x6f\x7e\x3d\xd9\x5d\xc4\x94\xc9\xdf\x0d\x6a\x15\x8d\xbd\xcd\x48\xc6\xde\x52\xc1\xd8\xb3\x90\x25\xdd\xc6\x73\xc4\xf0\x64\x01\x7e\xfe\x74\xa5\x6f\x1c\x1d\x82\x8b\xc8\xdd\x00\x30\x14\x33\xca\xf0\xb7\xf8\xe6\xa3\x1b\x44\xea\xd1\x67\xf7\xe3\xec\x74\xf6\xd5\xff\xfd\x65\xf0\xe9\xc3\xe4\xac\xef\x9d\xa3\x9b\xc0\x1d\xfc\xe3\x24\xa6\xcf\x91\xd4\x65\xaf\x28\x99\x78\xd8\x11\x35\x68\xb5\xbb\xbf\x11\x5a\x99\xc3\xd8\x69\x65\xb6\x30\x59\x48\x9f\xd7\xd1\x92\x07\x73\x00\x3d\x65\x1b\xa9\x63\x25\xa5\x74\xd8\xbf\xf9\xdc\xfd\x88\x4f\x6f\xbe\xdd\xfc\xfe\xea\xdb\xa7\x0f\xe8\xac\x4f\x3f\xa3\x99\xbb\x7b\x1a\x91\xa1\x78\xd3\xa7\x0d\xf5\xa3\x8d\x60\x7e\xb4\x0c\xf1\x23\x2b\x8f\xa4\x17\xb5\xa3\xec\xa4\x85\x25\x47\xa7\x6f\xe7\xaf\x8f\xbe\xbc\xfb\xf5\xf3\xfe\xe7\xe9\x6c\xf2\xee\x68\xfa\xe6\x82\xff\x34\x3f\xfd\x94\xe0\x5a\x5b\x58\x3c\x1e\xc6\xa6\x16\x54\x73\x26\x97\x65\x00\x69\x1d\x70\xe9\x37\xbd\x7f\xf5\xae\x7d\xfa\x7b\xfb\x68\x18\xdd\xac\x21\xb7\x90\xbe\x3f\x23\x6d\x83\xee\x44\x3b\xd2\x7d\x30\xc0\xed\x1e\xbe\xeb\xee\x7a\xc4\xf5\xfc\xdb\xee\xed\xc4\x39\xe0\x58\xc0\x3d\xee\x7d\x99\x1f\x9a\x4e\xc8\xc4\xf8\x61\x76\x49\x87\xde\x74\xcf\x3d\x3c\xbc\xed\x7a\xcc\x71\xe7\x83\xe9\x01\xf4\xc6\x07\xdc\x9b\x4c\xc9\x97\x5d\x77\x36\xe6\x5f\xfe\xf2\xff\xfe\x7a\xfa\xfb\xd5\xc5\x31\xf8\x2f\x8d\x71\x47\x41\xfc\x02\xbb\x88\x08\xb9\x66\x66\x08\x00\x73\xb0\x3d\xe8\x0e\xb6\x9f\x2b\x5a\xa8\x3f\x5f\xbd\xfd\x78\x79\x75\x7a\x71\xa9\x89\x21\x3f\xaa\x54\x76\xb2\xb0\x20\x1d\x48\xb5\xef\x4d\xf7\x28\xdb\xeb\xce\x71\xd8\x3d\xa0\x48\x2e\xdb\x8c\xdd\x38\xfd\x7d\x77\x3a\x11\x5f\x7a\xd0\xd9\x36\x95\x6c\x94\x1d\x56\xbd\x2a\x91\x30\xe4\xed\xdf\x2a\xe4\xc9\x15\xff\xc4\x16\xfb\x84\xdf\x8e\xfb\xfc\xdc\x7f\xfd\x65\x6f\xfc\x7b\x70\x72\xf0\x0a\xb6\xb6\xfe\x2f\x00\x00\xff\xff\x3e\xa2\xe6\x96\xa5\xd9\x00\x00") func connector_mgmtYamlBytes() ([]byte, error) { return bindataRead( @@ -93,7 +93,7 @@ func connector_mgmtYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "connector_mgmt.yaml", size: 55514, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + info := bindataFileInfo{name: "connector_mgmt.yaml", size: 55717, mode: os.FileMode(420), modTime: time.Unix(1, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/internal/connector/internal/handlers/connector_namespace.go b/internal/connector/internal/handlers/connector_namespace.go index 8024e8832..1b2a3a6f4 100644 --- a/internal/connector/internal/handlers/connector_namespace.go +++ b/internal/connector/internal/handlers/connector_namespace.go @@ -177,7 +177,7 @@ func (h *ConnectorNamespaceHandler) Get(w http.ResponseWriter, r *http.Request) } func (h *ConnectorNamespaceHandler) Update(w http.ResponseWriter, r *http.Request) { - var resource public.ConnectorNamespaceRequest + var resource public.ConnectorNamespacePatchRequest connectorNamespaceId := mux.Vars(r)["connector_namespace_id"] cfg := &handlers.HandlerConfig{ @@ -193,7 +193,12 @@ func (h *ConnectorNamespaceHandler) Update(w http.ResponseWriter, r *http.Reques } // Copy over the fields that support being updated... - existing.Name = resource.Name + if len(resource.Name) != 0 { + existing.Name = resource.Name + } else { + // name is the only updatable field for now + return nil, nil + } return nil, h.Service.Update(r.Context(), existing) }, diff --git a/internal/connector/internal/routes/route_loader.go b/internal/connector/internal/routes/route_loader.go index 38df6f00f..694ad6f30 100644 --- a/internal/connector/internal/routes/route_loader.go +++ b/internal/connector/internal/routes/route_loader.go @@ -124,7 +124,7 @@ func (s *options) AddRoutes(mainRouter *mux.Router) error { apiV1ConnectorNamespacesRouter.HandleFunc("/eval", s.ConnectorNamespaceHandler.CreateEvaluation).Methods(http.MethodPost) apiV1ConnectorNamespacesRouter.HandleFunc("", s.ConnectorNamespaceHandler.List).Methods(http.MethodGet) apiV1ConnectorNamespacesRouter.HandleFunc("/{connector_namespace_id}", s.ConnectorNamespaceHandler.Get).Methods(http.MethodGet) - apiV1ConnectorNamespacesRouter.HandleFunc("/{connector_namespace_id}", s.ConnectorNamespaceHandler.Update).Methods(http.MethodPut) + apiV1ConnectorNamespacesRouter.HandleFunc("/{connector_namespace_id}", s.ConnectorNamespaceHandler.Update).Methods(http.MethodPatch) apiV1ConnectorNamespacesRouter.HandleFunc("/{connector_namespace_id}", s.ConnectorNamespaceHandler.Delete).Methods(http.MethodDelete) apiV1ConnectorNamespacesRouter.Use(s.AuthorizeMiddleware.Authorize) diff --git a/openapi/connector_mgmt.yaml b/openapi/connector_mgmt.yaml index 3aa4fa17e..1fec4bdb2 100644 --- a/openapi/connector_mgmt.yaml +++ b/openapi/connector_mgmt.yaml @@ -907,7 +907,7 @@ paths: 500Example: $ref: "#/components/examples/500Example" description: Unexpected error occurred - put: + patch: tags: - Connector Namespaces security: @@ -916,11 +916,11 @@ paths: summary: udpate a connector namespace description: udpate a connector namespace requestBody: - description: Data to updated connector with + description: Data to update namespace with content: application/json: schema: - $ref: "#/components/schemas/ConnectorNamespaceRequest" + $ref: "#/components/schemas/ConnectorNamespacePatchRequest" required: true responses: "204": @@ -1425,8 +1425,6 @@ components: # Connector Namespaces # ConnectorNamespaceRequestMeta: - required: - - name type: object properties: name: @@ -1474,7 +1472,7 @@ components: - cluster_id - kind allOf: - - $ref: "#/components/schemas/ConnectorNamespaceEvalRequest" + - $ref: "#/components/schemas/ConnectorNamespaceRequestMeta" - type: object properties: cluster_id: @@ -1482,8 +1480,16 @@ components: kind: $ref: "#/components/schemas/ConnectorNamespaceTenantKind" + ConnectorNamespacePatchRequest: + description: A connector namespace patch request + allOf: + - $ref: "#/components/schemas/ConnectorNamespaceRequestMeta" + - type: object + ConnectorNamespaceEvalRequest: description: An evaluation connector namespace create request + required: + - name allOf: - $ref: "#/components/schemas/ConnectorNamespaceRequestMeta" From 51f37e31b5079de94ebc66ba942311b2b0fdbd42 Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Fri, 4 Mar 2022 00:02:53 -0800 Subject: [PATCH 11/12] fix(connectors): made cluster and namespace delete atomic db tx operations --- .../internal/services/connector_cluster.go | 119 +++++------------- .../internal/services/connector_namespaces.go | 68 +++++----- .../internal/workers/namespace_mgr.go | 3 +- 3 files changed, 61 insertions(+), 129 deletions(-) diff --git a/internal/connector/internal/services/connector_cluster.go b/internal/connector/internal/services/connector_cluster.go index edb3305cf..a4d530c17 100644 --- a/internal/connector/internal/services/connector_cluster.go +++ b/internal/connector/internal/services/connector_cluster.go @@ -195,101 +195,40 @@ func (k *connectorClusterService) Delete(ctx context.Context, id string) *errors return errors.Unauthenticated("user not authenticated") } - dbConn := k.connectionFactory.New() - var resource dbapi.ConnectorCluster - if err := dbConn.Where("owner = ? AND id = ?", owner, id).First(&resource).Error; err != nil { - return services.HandleGetError("Connector cluster", "id", id, err) - } + if err := k.connectionFactory.New().Transaction(func(dbConn *gorm.DB) error { - if err := dbConn.Delete(&resource).Error; err != nil { - return errors.GeneralError("unable to delete connector with id %s: %s", resource.ID, err) - } - - // Delete all deployments assigned to the cluster.. - dbConn = k.connectionFactory.New() - { - rows, err := dbConn. - Model(&dbapi.ConnectorDeployment{}). - Select("id"). - Where("cluster_id = ?", id). - Rows() - if err != nil { - return errors.GeneralError("unable find deployments of cluster %s: %s", id, err) - } - defer rows.Close() - for rows.Next() { - deployment := dbapi.ConnectorDeployment{} - err := dbConn.ScanRows(rows, &deployment) - if err != nil { - return errors.GeneralError("Unable to scan connector deployment: %s", err) - } - deploymentStatus := dbapi.ConnectorDeploymentStatus{ - Meta: api.Meta{ - ID: deployment.ID, - }, - } - if err := dbConn.Delete(&deploymentStatus).Error; err != nil { - return errors.GeneralError("failed to delete connector deployment status: %s", err) - } - if err := dbConn.Delete(&deployment).Error; err != nil { - return errors.GeneralError("failed to delete connector deployment: %s", err) - } + var resource dbapi.ConnectorCluster + if err := dbConn.Where("owner = ? AND id = ?", owner, id).First(&resource).Error; err != nil { + return services.HandleGetError("Connector cluster", "id", id, err) } - } - // Delete all namespaces assigned to the cluster.. - dbConn = k.connectionFactory.New() - { - rows, err := dbConn. - Model(&dbapi.ConnectorNamespace{}). - Select("id"). - Where("cluster_id = ?", id). - Rows() - if err != nil { - return errors.GeneralError("unable find namespaces of cluster %s: %s", id, err) - } - defer rows.Close() - for rows.Next() { - namespace := dbapi.ConnectorNamespace{} - err := dbConn.ScanRows(rows, &namespace) - if err != nil { - return errors.GeneralError("Unable to scan connector namespace: %s", err) - } - if err := dbConn.Delete(&namespace).Error; err != nil { - return errors.GeneralError("failed to delete connector namespace: %s", err) - } + // delete cluster + if err := dbConn.Delete(&dbapi.ConnectorCluster{}, "id = ?", id).Error; err != nil { + return services.HandleDeleteError("Connector cluster", "id", id, err) } - } - // Clear the cluster from any connectors that were using it. - { - rows, err := dbConn. - Model(&dbapi.Connector{}). - Select("connectors.id"). - Joins("INNER JOIN connector_namespaces ON connector_namespaces.id = connectors.namespace_id"). - Where("connector_namespaces.cluster_id = ?", id). - Rows() - if err != nil { - return errors.GeneralError("unable find connector using cluster %s: %s", id, err) - } - defer rows.Close() - for rows.Next() { - connector := dbapi.Connector{} - err := dbConn.ScanRows(rows, &connector) - if err != nil { - return errors.GeneralError("Unable to scan connector: %s", err) - } + // Delete all deployments assigned to the cluster.. + if err := dbConn.Delete(&dbapi.ConnectorDeploymentStatus{}, "id IN (?)", + dbConn.Table("connector_deployments").Select("id").Where("cluster_id = ?", id)).Error; err != nil { + return services.HandleDeleteError("Connector deployment status", "cluster_id", id, err) + } + if err := dbConn.Delete(&dbapi.ConnectorDeployment{}, "cluster_id = ?", id).Error; err != nil { + return services.HandleDeleteError("Connector deployment", "cluster_id", id, err) + } - if err := dbConn.Model(&connector).Update("namespace_id", nil).Error; err != nil { - return errors.GeneralError("failed to update connector: %s", err) - } + // Delete all namespaces assigned to the cluster.. + if err := dbConn.Delete(&dbapi.ConnectorNamespace{}, "cluster_id = ?", id).Error; err != nil { + return services.HandleDeleteError("Connector namespace", "cluster_id", id, err) + } - status := dbapi.ConnectorStatus{} - status.ID = connector.ID - if err := dbConn.Model(&status).Update("phase", "assigning").Error; err != nil { - return errors.GeneralError("failed to update connector status: %s", err) - } + // Clear the cluster from any connectors that were using it. + if err := removeConnectorsFromNamespace(dbConn, "connector_namespaces.cluster_id = ?", id); err != nil { + return err } + + return nil + }); err != nil { + return services.HandleDeleteError("Connector cluster", "id", id, err) } glog.V(5).Infof("Removing agent service account for connector cluster %s", id) @@ -875,9 +814,9 @@ func (k *connectorClusterService) GetAvailableDeploymentOperatorUpgrades(listArg type Result struct { ConnectorID string DeploymentID string - ConnectorTypeID string - NamespaceID string - Channel string + ConnectorTypeID string + NamespaceID string + Channel string ConnectorOperators api.JSON } diff --git a/internal/connector/internal/services/connector_namespaces.go b/internal/connector/internal/services/connector_namespaces.go index aa8eac865..374d46164 100644 --- a/internal/connector/internal/services/connector_namespaces.go +++ b/internal/connector/internal/services/connector_namespaces.go @@ -12,6 +12,7 @@ import ( "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/services" coreServices "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/services/queryparser" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/services/signalbus" + "gorm.io/gorm" "math/rand" "time" ) @@ -204,45 +205,23 @@ func (k *connectorNamespaceService) List(ctx context.Context, clusterIDs []strin } func (k *connectorNamespaceService) Delete(namespaceId string) *errors.ServiceError { - dbConn := k.connectionFactory.New() - dbConn = dbConn.Where("id = ?", namespaceId) - if err := dbConn.Delete(&dbapi.ConnectorNamespace{}).Error; err != nil { - return errors.GeneralError("failed to delete connector namespace: %v", err) - } + if err := k.connectionFactory.New().Transaction(func(dbConn *gorm.DB) error { - // Clear the namespace from any connectors that were using it. - // TODO do this asynchronously in a namespace reconciler - dbConn = k.connectionFactory.New() - { - rows, err := dbConn. - Model(&dbapi.Connector{}). - Select("id"). - Where("namespace_id = ?", namespaceId). - Rows() - if err != nil { - return errors.GeneralError("Unable to find connector using namespace %s: %s", namespaceId, err) + if err := dbConn.Where("id = ?", namespaceId). + First(&dbapi.ConnectorNamespace{}).Error; err != nil { + return services.HandleGetError("Connector cluster", "id", namespaceId, err) } - defer rows.Close() - for rows.Next() { - connector := dbapi.Connector{} - err := dbConn.ScanRows(rows, &connector) - if err != nil { - return errors.GeneralError("Unable to scan connector: %s", err) - } - - if err := dbConn.Model(&connector).Update("namespace_id", nil).Error; err != nil { - return errors.GeneralError("Failed to update connector: %s", err) - } - - status := dbapi.ConnectorStatus{} - status.ID = connector.ID - status.NamespaceID = nil - if err := dbConn.Model(&status). - Update("namespace_id", nil). - Update("phase", dbapi.ConnectorStatusPhaseAssigning).Error; err != nil { - return errors.GeneralError("Failed to update connector status: %s", err) - } + + // TODO do this asynchronously in a namespace reconciler + if err := dbConn.Where("id = ?", namespaceId). + Delete(&dbapi.ConnectorNamespace{}).Error; err != nil { + return services.HandleDeleteError("Connector namespace", "id", namespaceId, err) } + + // Clear the namespace from any connectors that were using it. + return removeConnectorsFromNamespace(dbConn, "connector_namespaces.id = ?", namespaceId) + }); err != nil { + return services.HandleDeleteError("Connector namespace", "id", namespaceId, err) } // TODO: increment connector namespace metrics @@ -271,7 +250,7 @@ func (k *connectorNamespaceService) CreateDefaultNamespace(ctx context.Context, }, }, ClusterId: connectorCluster.ID, - Kind: kind, + Kind: kind, }, owner, organisationId) if err != nil { @@ -288,3 +267,18 @@ func (k *connectorNamespaceService) GetExpiredNamespaces() (dbapi.ConnectorNames } return result, nil } + +func removeConnectorsFromNamespace(dbConn *gorm.DB, query interface{}, values ...interface{}) error { + namespaceSubQuery :=dbConn.Table("connector_namespaces").Select("id").Where(query, values) + if err := dbConn.Select("namespace_id"). + Where("namespace_id IN (?)", namespaceSubQuery). + Updates(&dbapi.Connector{ NamespaceId: nil}).Error; err != nil { + return services.HandleUpdateError("Connector", err) + } + if err := dbConn.Select("namespace_id", "phase"). + Where("namespace_id IN (?)", namespaceSubQuery). + Updates(&dbapi.ConnectorStatus{NamespaceID: nil, Phase: "assigning"}).Error; err != nil { + return services.HandleUpdateError("Connector", err) + } + return nil +} diff --git a/internal/connector/internal/workers/namespace_mgr.go b/internal/connector/internal/workers/namespace_mgr.go index a294b3472..0f57ff911 100644 --- a/internal/connector/internal/workers/namespace_mgr.go +++ b/internal/connector/internal/workers/namespace_mgr.go @@ -2,7 +2,6 @@ package workers import ( "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/internal/connector/internal/services" - "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/errors" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/services/signalbus" "github.com/bf2fc6cc711aee1a0c2a/kas-fleet-manager/pkg/workers" "github.com/golang/glog" @@ -56,7 +55,7 @@ func (m *NamespaceManager) Reconcile() []error { for _, namespace := range namespaces { id := namespace.ID if err := m.namespaceService.Delete(id); err != nil { - errs = append(errs, errors.GeneralError("Error deleting namespace %s: %s", id, err)) + errs = append(errs, err) success-- } } From 307aa4763a16be2b2d871a97556558a3531307ad Mon Sep 17 00:00:00 2001 From: Dhiraj Bokde Date: Fri, 4 Mar 2022 10:14:47 -0800 Subject: [PATCH 12/12] fix(connector): typo in namespace delete error message --- internal/connector/internal/services/connector_namespaces.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/connector/internal/services/connector_namespaces.go b/internal/connector/internal/services/connector_namespaces.go index 374d46164..e0d48b64e 100644 --- a/internal/connector/internal/services/connector_namespaces.go +++ b/internal/connector/internal/services/connector_namespaces.go @@ -209,7 +209,7 @@ func (k *connectorNamespaceService) Delete(namespaceId string) *errors.ServiceEr if err := dbConn.Where("id = ?", namespaceId). First(&dbapi.ConnectorNamespace{}).Error; err != nil { - return services.HandleGetError("Connector cluster", "id", namespaceId, err) + return services.HandleGetError("Connector namespace", "id", namespaceId, err) } // TODO do this asynchronously in a namespace reconciler