feat: add cluster api in manager (#2288)

Signed-off-by: Gaius <gaius.qi@gmail.com>
This commit is contained in:
Gaius 2023-04-20 16:53:55 +08:00
parent e34b66f58c
commit aa90014804
No known key found for this signature in database
GPG Key ID: 8B4E5D1290FA2FFB
15 changed files with 1866 additions and 52 deletions

View File

@ -391,6 +391,227 @@ const docTemplate = `{
}
}
},
"/clusters": {
"get": {
"description": "Get Clusters",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Cluster"
],
"summary": "Get Clusters",
"parameters": [
{
"type": "integer",
"default": 0,
"description": "current page",
"name": "page",
"in": "query",
"required": true
},
{
"maximum": 50,
"minimum": 2,
"type": "integer",
"default": 10,
"description": "return max item count, default 10, max 50",
"name": "per_page",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.GetClusterResponse"
}
}
},
"400": {
"description": "Bad Request"
},
"404": {
"description": "Not Found"
},
"500": {
"description": "Internal Server Error"
}
}
},
"post": {
"description": "Create by json config",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Cluster"
],
"summary": "Create Cluster",
"parameters": [
{
"description": "Cluster",
"name": "Cluster",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.CreateClusterRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.CreateClusterResponse"
}
},
"400": {
"description": "Bad Request"
},
"404": {
"description": "Not Found"
},
"500": {
"description": "Internal Server Error"
}
}
}
},
"/clusters/{id}": {
"get": {
"description": "Get Cluster by id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Cluster"
],
"summary": "Get Cluster",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.GetClusterResponse"
}
},
"400": {
"description": "Bad Request"
},
"404": {
"description": "Not Found"
},
"500": {
"description": "Internal Server Error"
}
}
},
"delete": {
"description": "Destroy by id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Cluster"
],
"summary": "Destroy Cluster",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
},
"400": {
"description": "Bad Request"
},
"404": {
"description": "Not Found"
},
"500": {
"description": "Internal Server Error"
}
}
},
"patch": {
"description": "Update by json config",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Cluster"
],
"summary": "Update Cluster",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Cluster",
"name": "Cluster",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.UpdateClusterRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.UpdateClusterResponse"
}
},
"400": {
"description": "Bad Request"
},
"404": {
"description": "Not Found"
},
"500": {
"description": "Internal Server Error"
}
}
}
},
"/configs": {
"get": {
"description": "Get Configs",
@ -3174,9 +3395,6 @@ const docTemplate = `{
"id": {
"type": "integer"
},
"is_default": {
"type": "boolean"
},
"jobs": {
"type": "array",
"items": {
@ -3307,6 +3525,79 @@ const docTemplate = `{
}
}
},
"d7y_io_dragonfly_v2_manager_types.CreateClusterRequest": {
"type": "object",
"required": [
"name",
"peer_cluster_config",
"scheduler_cluster_config",
"seed_peer_cluster_config"
],
"properties": {
"bio": {
"type": "string"
},
"is_default": {
"type": "boolean"
},
"name": {
"type": "string"
},
"peer_cluster_config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterClientConfig"
},
"scheduler_cluster_config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterConfig"
},
"scopes": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterScopes"
},
"seed_peer_cluster_config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig"
}
}
},
"d7y_io_dragonfly_v2_manager_types.CreateClusterResponse": {
"type": "object",
"properties": {
"bio": {
"type": "string"
},
"createdAt": {
"type": "string"
},
"id": {
"type": "integer"
},
"isDefault": {
"type": "boolean"
},
"name": {
"type": "string"
},
"peerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterClientConfig"
},
"schedulerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterConfig"
},
"schedulerClusterID": {
"type": "integer"
},
"scopes": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterScopes"
},
"seedPeerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig"
},
"seedPeerClusterID": {
"type": "integer"
},
"updatedAt": {
"type": "string"
}
}
},
"d7y_io_dragonfly_v2_manager_types.CreateConfigRequest": {
"type": "object",
"required": [
@ -3492,9 +3783,6 @@ const docTemplate = `{
"config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig"
},
"is_default": {
"type": "boolean"
},
"name": {
"type": "string"
}
@ -3600,6 +3888,47 @@ const docTemplate = `{
}
}
},
"d7y_io_dragonfly_v2_manager_types.GetClusterResponse": {
"type": "object",
"properties": {
"bio": {
"type": "string"
},
"createdAt": {
"type": "string"
},
"id": {
"type": "integer"
},
"isDefault": {
"type": "boolean"
},
"name": {
"type": "string"
},
"peerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterClientConfig"
},
"schedulerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterConfig"
},
"schedulerClusterID": {
"type": "integer"
},
"scopes": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterScopes"
},
"seedPeerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig"
},
"seedPeerClusterID": {
"type": "integer"
},
"updatedAt": {
"type": "string"
}
}
},
"d7y_io_dragonfly_v2_manager_types.GetV1PreheatResponse": {
"type": "object",
"properties": {
@ -3787,6 +4116,73 @@ const docTemplate = `{
}
}
},
"d7y_io_dragonfly_v2_manager_types.UpdateClusterRequest": {
"type": "object",
"properties": {
"bio": {
"type": "string"
},
"is_default": {
"type": "boolean"
},
"name": {
"type": "string"
},
"peer_cluster_config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterClientConfig"
},
"scheduler_cluster_config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterConfig"
},
"scopes": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterScopes"
},
"seed_peer_cluster_config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig"
}
}
},
"d7y_io_dragonfly_v2_manager_types.UpdateClusterResponse": {
"type": "object",
"properties": {
"bio": {
"type": "string"
},
"createdAt": {
"type": "string"
},
"id": {
"type": "integer"
},
"isDefault": {
"type": "boolean"
},
"name": {
"type": "string"
},
"peerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterClientConfig"
},
"schedulerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterConfig"
},
"schedulerClusterID": {
"type": "integer"
},
"scopes": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterScopes"
},
"seedPeerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig"
},
"seedPeerClusterID": {
"type": "integer"
},
"updatedAt": {
"type": "string"
}
}
},
"d7y_io_dragonfly_v2_manager_types.UpdateConfigRequest": {
"type": "object",
"properties": {
@ -3903,9 +4299,6 @@ const docTemplate = `{
"config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig"
},
"is_default": {
"type": "boolean"
},
"name": {
"type": "string"
}

View File

@ -385,6 +385,227 @@
}
}
},
"/clusters": {
"get": {
"description": "Get Clusters",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Cluster"
],
"summary": "Get Clusters",
"parameters": [
{
"type": "integer",
"default": 0,
"description": "current page",
"name": "page",
"in": "query",
"required": true
},
{
"maximum": 50,
"minimum": 2,
"type": "integer",
"default": 10,
"description": "return max item count, default 10, max 50",
"name": "per_page",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.GetClusterResponse"
}
}
},
"400": {
"description": "Bad Request"
},
"404": {
"description": "Not Found"
},
"500": {
"description": "Internal Server Error"
}
}
},
"post": {
"description": "Create by json config",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Cluster"
],
"summary": "Create Cluster",
"parameters": [
{
"description": "Cluster",
"name": "Cluster",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.CreateClusterRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.CreateClusterResponse"
}
},
"400": {
"description": "Bad Request"
},
"404": {
"description": "Not Found"
},
"500": {
"description": "Internal Server Error"
}
}
}
},
"/clusters/{id}": {
"get": {
"description": "Get Cluster by id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Cluster"
],
"summary": "Get Cluster",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.GetClusterResponse"
}
},
"400": {
"description": "Bad Request"
},
"404": {
"description": "Not Found"
},
"500": {
"description": "Internal Server Error"
}
}
},
"delete": {
"description": "Destroy by id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Cluster"
],
"summary": "Destroy Cluster",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
},
"400": {
"description": "Bad Request"
},
"404": {
"description": "Not Found"
},
"500": {
"description": "Internal Server Error"
}
}
},
"patch": {
"description": "Update by json config",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Cluster"
],
"summary": "Update Cluster",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Cluster",
"name": "Cluster",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.UpdateClusterRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.UpdateClusterResponse"
}
},
"400": {
"description": "Bad Request"
},
"404": {
"description": "Not Found"
},
"500": {
"description": "Internal Server Error"
}
}
}
},
"/configs": {
"get": {
"description": "Get Configs",
@ -3168,9 +3389,6 @@
"id": {
"type": "integer"
},
"is_default": {
"type": "boolean"
},
"jobs": {
"type": "array",
"items": {
@ -3301,6 +3519,79 @@
}
}
},
"d7y_io_dragonfly_v2_manager_types.CreateClusterRequest": {
"type": "object",
"required": [
"name",
"peer_cluster_config",
"scheduler_cluster_config",
"seed_peer_cluster_config"
],
"properties": {
"bio": {
"type": "string"
},
"is_default": {
"type": "boolean"
},
"name": {
"type": "string"
},
"peer_cluster_config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterClientConfig"
},
"scheduler_cluster_config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterConfig"
},
"scopes": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterScopes"
},
"seed_peer_cluster_config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig"
}
}
},
"d7y_io_dragonfly_v2_manager_types.CreateClusterResponse": {
"type": "object",
"properties": {
"bio": {
"type": "string"
},
"createdAt": {
"type": "string"
},
"id": {
"type": "integer"
},
"isDefault": {
"type": "boolean"
},
"name": {
"type": "string"
},
"peerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterClientConfig"
},
"schedulerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterConfig"
},
"schedulerClusterID": {
"type": "integer"
},
"scopes": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterScopes"
},
"seedPeerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig"
},
"seedPeerClusterID": {
"type": "integer"
},
"updatedAt": {
"type": "string"
}
}
},
"d7y_io_dragonfly_v2_manager_types.CreateConfigRequest": {
"type": "object",
"required": [
@ -3486,9 +3777,6 @@
"config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig"
},
"is_default": {
"type": "boolean"
},
"name": {
"type": "string"
}
@ -3594,6 +3882,47 @@
}
}
},
"d7y_io_dragonfly_v2_manager_types.GetClusterResponse": {
"type": "object",
"properties": {
"bio": {
"type": "string"
},
"createdAt": {
"type": "string"
},
"id": {
"type": "integer"
},
"isDefault": {
"type": "boolean"
},
"name": {
"type": "string"
},
"peerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterClientConfig"
},
"schedulerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterConfig"
},
"schedulerClusterID": {
"type": "integer"
},
"scopes": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterScopes"
},
"seedPeerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig"
},
"seedPeerClusterID": {
"type": "integer"
},
"updatedAt": {
"type": "string"
}
}
},
"d7y_io_dragonfly_v2_manager_types.GetV1PreheatResponse": {
"type": "object",
"properties": {
@ -3781,6 +4110,73 @@
}
}
},
"d7y_io_dragonfly_v2_manager_types.UpdateClusterRequest": {
"type": "object",
"properties": {
"bio": {
"type": "string"
},
"is_default": {
"type": "boolean"
},
"name": {
"type": "string"
},
"peer_cluster_config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterClientConfig"
},
"scheduler_cluster_config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterConfig"
},
"scopes": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterScopes"
},
"seed_peer_cluster_config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig"
}
}
},
"d7y_io_dragonfly_v2_manager_types.UpdateClusterResponse": {
"type": "object",
"properties": {
"bio": {
"type": "string"
},
"createdAt": {
"type": "string"
},
"id": {
"type": "integer"
},
"isDefault": {
"type": "boolean"
},
"name": {
"type": "string"
},
"peerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterClientConfig"
},
"schedulerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterConfig"
},
"schedulerClusterID": {
"type": "integer"
},
"scopes": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterScopes"
},
"seedPeerClusterConfig": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig"
},
"seedPeerClusterID": {
"type": "integer"
},
"updatedAt": {
"type": "string"
}
}
},
"d7y_io_dragonfly_v2_manager_types.UpdateConfigRequest": {
"type": "object",
"properties": {
@ -3897,9 +4293,6 @@
"config": {
"$ref": "#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig"
},
"is_default": {
"type": "boolean"
},
"name": {
"type": "string"
}

View File

@ -186,8 +186,6 @@ definitions:
type: string
id:
type: integer
is_default:
type: boolean
jobs:
items:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_models.Job'
@ -275,6 +273,55 @@ definitions:
required:
- name
type: object
d7y_io_dragonfly_v2_manager_types.CreateClusterRequest:
properties:
bio:
type: string
is_default:
type: boolean
name:
type: string
peer_cluster_config:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterClientConfig'
scheduler_cluster_config:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterConfig'
scopes:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterScopes'
seed_peer_cluster_config:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig'
required:
- name
- peer_cluster_config
- scheduler_cluster_config
- seed_peer_cluster_config
type: object
d7y_io_dragonfly_v2_manager_types.CreateClusterResponse:
properties:
bio:
type: string
createdAt:
type: string
id:
type: integer
isDefault:
type: boolean
name:
type: string
peerClusterConfig:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterClientConfig'
schedulerClusterConfig:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterConfig'
schedulerClusterID:
type: integer
scopes:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterScopes'
seedPeerClusterConfig:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig'
seedPeerClusterID:
type: integer
updatedAt:
type: string
type: object
d7y_io_dragonfly_v2_manager_types.CreateConfigRequest:
properties:
bio:
@ -398,8 +445,6 @@ definitions:
type: string
config:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig'
is_default:
type: boolean
name:
type: string
required:
@ -475,6 +520,33 @@ definitions:
- action
- object
type: object
d7y_io_dragonfly_v2_manager_types.GetClusterResponse:
properties:
bio:
type: string
createdAt:
type: string
id:
type: integer
isDefault:
type: boolean
name:
type: string
peerClusterConfig:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterClientConfig'
schedulerClusterConfig:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterConfig'
schedulerClusterID:
type: integer
scopes:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterScopes'
seedPeerClusterConfig:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig'
seedPeerClusterID:
type: integer
updatedAt:
type: string
type: object
d7y_io_dragonfly_v2_manager_types.GetV1PreheatResponse:
properties:
finishTime:
@ -605,6 +677,50 @@ definitions:
required:
- user_id
type: object
d7y_io_dragonfly_v2_manager_types.UpdateClusterRequest:
properties:
bio:
type: string
is_default:
type: boolean
name:
type: string
peer_cluster_config:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterClientConfig'
scheduler_cluster_config:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterConfig'
scopes:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterScopes'
seed_peer_cluster_config:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig'
type: object
d7y_io_dragonfly_v2_manager_types.UpdateClusterResponse:
properties:
bio:
type: string
createdAt:
type: string
id:
type: integer
isDefault:
type: boolean
name:
type: string
peerClusterConfig:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterClientConfig'
schedulerClusterConfig:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterConfig'
schedulerClusterID:
type: integer
scopes:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SchedulerClusterScopes'
seedPeerClusterConfig:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig'
seedPeerClusterID:
type: integer
updatedAt:
type: string
type: object
d7y_io_dragonfly_v2_manager_types.UpdateConfigRequest:
properties:
bio:
@ -681,8 +797,6 @@ definitions:
type: string
config:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.SeedPeerClusterConfig'
is_default:
type: boolean
name:
type: string
type: object
@ -988,6 +1102,154 @@ paths:
summary: Get Bucket
tags:
- Bucket
/clusters:
get:
consumes:
- application/json
description: Get Clusters
parameters:
- default: 0
description: current page
in: query
name: page
required: true
type: integer
- default: 10
description: return max item count, default 10, max 50
in: query
maximum: 50
minimum: 2
name: per_page
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.GetClusterResponse'
type: array
"400":
description: Bad Request
"404":
description: Not Found
"500":
description: Internal Server Error
summary: Get Clusters
tags:
- Cluster
post:
consumes:
- application/json
description: Create by json config
parameters:
- description: Cluster
in: body
name: Cluster
required: true
schema:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.CreateClusterRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.CreateClusterResponse'
"400":
description: Bad Request
"404":
description: Not Found
"500":
description: Internal Server Error
summary: Create Cluster
tags:
- Cluster
/clusters/{id}:
delete:
consumes:
- application/json
description: Destroy by id
parameters:
- description: id
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
"400":
description: Bad Request
"404":
description: Not Found
"500":
description: Internal Server Error
summary: Destroy Cluster
tags:
- Cluster
get:
consumes:
- application/json
description: Get Cluster by id
parameters:
- description: id
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.GetClusterResponse'
"400":
description: Bad Request
"404":
description: Not Found
"500":
description: Internal Server Error
summary: Get Cluster
tags:
- Cluster
patch:
consumes:
- application/json
description: Update by json config
parameters:
- description: id
in: path
name: id
required: true
type: string
- description: Cluster
in: body
name: Cluster
required: true
schema:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.UpdateClusterRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/d7y_io_dragonfly_v2_manager_types.UpdateClusterResponse'
"400":
description: Bad Request
"404":
description: Not Found
"500":
description: Internal Server Error
summary: Update Cluster
tags:
- Cluster
/configs:
get:
consumes:

View File

@ -135,7 +135,6 @@ func seed(cfg *config.Config, db *gorm.DB) error {
Config: map[string]any{
"load_limit": schedulerconfig.DefaultSeedPeerConcurrentUploadLimit,
},
IsDefault: true,
}).Error; err != nil {
return err
}

171
manager/handlers/cluster.go Normal file
View File

@ -0,0 +1,171 @@
/*
* Copyright 2023 The Dragonfly Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package handlers
import (
"net/http"
"github.com/gin-gonic/gin"
// nolint
_ "d7y.io/dragonfly/v2/manager/models"
"d7y.io/dragonfly/v2/manager/types"
)
// @Summary Create Cluster
// @Description Create by json config
// @Tags Cluster
// @Accept json
// @Produce json
// @Param Cluster body types.CreateClusterRequest true "Cluster"
// @Success 200 {object} types.CreateClusterResponse
// @Failure 400
// @Failure 404
// @Failure 500
// @Router /clusters [post]
func (h *Handlers) CreateCluster(ctx *gin.Context) {
var json types.CreateClusterRequest
if err := ctx.ShouldBindJSON(&json); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
cluster, err := h.service.CreateCluster(ctx.Request.Context(), json)
if err != nil {
ctx.Error(err) // nolint: errcheck
return
}
ctx.JSON(http.StatusOK, cluster)
}
// @Summary Destroy Cluster
// @Description Destroy by id
// @Tags Cluster
// @Accept json
// @Produce json
// @Param id path string true "id"
// @Success 200
// @Failure 400
// @Failure 404
// @Failure 500
// @Router /clusters/{id} [delete]
func (h *Handlers) DestroyCluster(ctx *gin.Context) {
var params types.ClusterParams
if err := ctx.ShouldBindUri(&params); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
if err := h.service.DestroyCluster(ctx.Request.Context(), params.ID); err != nil {
ctx.Error(err) // nolint: errcheck
return
}
ctx.Status(http.StatusOK)
}
// @Summary Update Cluster
// @Description Update by json config
// @Tags Cluster
// @Accept json
// @Produce json
// @Param id path string true "id"
// @Param Cluster body types.UpdateClusterRequest true "Cluster"
// @Success 200 {object} types.UpdateClusterResponse
// @Failure 400
// @Failure 404
// @Failure 500
// @Router /clusters/{id} [patch]
func (h *Handlers) UpdateCluster(ctx *gin.Context) {
var params types.ClusterParams
if err := ctx.ShouldBindUri(&params); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
var json types.UpdateClusterRequest
if err := ctx.ShouldBindJSON(&json); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
cluster, err := h.service.UpdateCluster(ctx.Request.Context(), params.ID, json)
if err != nil {
ctx.Error(err) // nolint: errcheck
return
}
ctx.JSON(http.StatusOK, cluster)
}
// @Summary Get Cluster
// @Description Get Cluster by id
// @Tags Cluster
// @Accept json
// @Produce json
// @Param id path string true "id"
// @Success 200 {object} types.GetClusterResponse
// @Failure 400
// @Failure 404
// @Failure 500
// @Router /clusters/{id} [get]
func (h *Handlers) GetCluster(ctx *gin.Context) {
var params types.ClusterParams
if err := ctx.ShouldBindUri(&params); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
cluster, err := h.service.GetCluster(ctx.Request.Context(), params.ID)
if err != nil {
ctx.Error(err) // nolint: errcheck
return
}
ctx.JSON(http.StatusOK, cluster)
}
// @Summary Get Clusters
// @Description Get Clusters
// @Tags Cluster
// @Accept json
// @Produce json
// @Param page query int true "current page" default(0)
// @Param per_page query int true "return max item count, default 10, max 50" default(10) minimum(2) maximum(50)
// @Success 200 {object} []types.GetClusterResponse
// @Failure 400
// @Failure 404
// @Failure 500
// @Router /clusters [get]
func (h *Handlers) GetClusters(ctx *gin.Context) {
var query types.GetClustersQuery
if err := ctx.ShouldBindQuery(&query); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
h.setPaginationDefault(&query.Page, &query.PerPage)
clusters, count, err := h.service.GetClusters(ctx.Request.Context(), query)
if err != nil {
ctx.Error(err) // nolint: errcheck
return
}
h.setPaginationLinkHeader(ctx, query.Page, query.PerPage, int(count))
ctx.JSON(http.StatusOK, clusters)
}

View File

@ -21,7 +21,6 @@ type SeedPeerCluster struct {
Name string `gorm:"column:name;type:varchar(256);index:uk_seed_peer_cluster_name,unique;not null;comment:name" json:"name"`
BIO string `gorm:"column:bio;type:varchar(1024);comment:biography" json:"bio"`
Config JSONMap `gorm:"column:config;not null;comment:configuration" json:"config"`
IsDefault bool `gorm:"column:is_default;not null;default:false;comment:default seed peer cluster" json:"is_default"`
SchedulerClusters []SchedulerCluster `gorm:"many2many:seed_peer_cluster_scheduler_cluster;" json:"scheduler_clusters"`
SeedPeers []SeedPeer `json:"-"`
Jobs []Job `gorm:"many2many:job_seed_peer_cluster;" json:"jobs"`

View File

@ -126,6 +126,14 @@ func Init(cfg *config.Config, logDir string, service service.Service, enforcer *
oa.GET(":id", h.GetOauth)
oa.GET("", h.GetOauths)
// Cluster
c := apiv1.Group("/clusters", jwt.MiddlewareFunc(), rbac)
c.POST("", h.CreateCluster)
c.DELETE(":id", h.DestroyCluster)
c.PATCH(":id", h.UpdateCluster)
c.GET(":id", h.GetCluster)
c.GET("", h.GetClusters)
// Scheduler Cluster
sc := apiv1.Group("/scheduler-clusters", jwt.MiddlewareFunc(), rbac)
sc.POST("", h.CreateSchedulerCluster)

365
manager/service/cluster.go Normal file
View File

@ -0,0 +1,365 @@
/*
* Copyright 2023 The Dragonfly Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package service
import (
"context"
"errors"
"d7y.io/dragonfly/v2/manager/models"
"d7y.io/dragonfly/v2/manager/types"
"d7y.io/dragonfly/v2/pkg/structure"
)
func (s *service) CreateCluster(ctx context.Context, json types.CreateClusterRequest) (*types.CreateClusterResponse, error) {
schedulerClusterConfig, err := structure.StructToMap(json.SchedulerClusterConfig)
if err != nil {
return nil, err
}
seedPeerClusterConfig, err := structure.StructToMap(json.SeedPeerClusterConfig)
if err != nil {
return nil, err
}
peerClusterConfig, err := structure.StructToMap(json.PeerClusterConfig)
if err != nil {
return nil, err
}
scopes, err := structure.StructToMap(json.Scopes)
if err != nil {
return nil, err
}
// Create cluster with transaction.
tx := s.db.WithContext(ctx).Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Error; err != nil {
return nil, err
}
schedulerCluster := models.SchedulerCluster{
Name: json.Name,
BIO: json.BIO,
Config: schedulerClusterConfig,
ClientConfig: peerClusterConfig,
Scopes: scopes,
IsDefault: json.IsDefault,
}
if err := tx.WithContext(ctx).Create(&schedulerCluster).Error; err != nil {
tx.Rollback()
return nil, err
}
seedPeerCluster := models.SeedPeerCluster{
Name: json.Name,
BIO: json.BIO,
Config: seedPeerClusterConfig,
}
if err := tx.WithContext(ctx).Create(&seedPeerCluster).Error; err != nil {
tx.Rollback()
return nil, err
}
if err := tx.WithContext(ctx).Model(&seedPeerCluster).Association("SchedulerClusters").Append(&schedulerCluster); err != nil {
tx.Rollback()
return nil, err
}
if tx.Commit().Error != nil {
return nil, err
}
return &types.CreateClusterResponse{
ID: schedulerCluster.ID,
Name: schedulerCluster.Name,
BIO: schedulerCluster.BIO,
Scopes: json.Scopes,
SchedulerClusterID: schedulerCluster.ID,
SeedPeerClusterID: seedPeerCluster.ID,
SchedulerClusterConfig: json.SchedulerClusterConfig,
SeedPeerClusterConfig: json.SeedPeerClusterConfig,
PeerClusterConfig: json.PeerClusterConfig,
CreatedAt: schedulerCluster.CreatedAt,
UpdatedAt: schedulerCluster.UpdatedAt,
IsDefault: schedulerCluster.IsDefault,
}, nil
}
func (s *service) DestroyCluster(ctx context.Context, id uint) error {
schedulerCluster := models.SchedulerCluster{}
if err := s.db.WithContext(ctx).Preload("Schedulers").First(&schedulerCluster, id).Error; err != nil {
return err
}
if len(schedulerCluster.Schedulers) != 0 {
return errors.New("scheduler cluster exists scheduler")
}
seedPeerClusters := []models.SeedPeerCluster{}
if err := s.db.WithContext(ctx).Model(&schedulerCluster).Association("SeedPeerClusters").Find(&seedPeerClusters); err != nil {
return err
}
// Delete cluster with transaction.
tx := s.db.WithContext(ctx).Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Error; err != nil {
return err
}
for _, seedPeerCluster := range seedPeerClusters {
if len(seedPeerCluster.SeedPeers) != 0 {
tx.Rollback()
return errors.New("seed peer cluster exists seed peer")
}
if err := tx.WithContext(ctx).Delete(&models.SeedPeerCluster{}, seedPeerCluster.ID).Error; err != nil {
tx.Rollback()
return err
}
}
if err := tx.WithContext(ctx).Model(&schedulerCluster).Association("SeedPeerClusters").Clear(); err != nil {
tx.Rollback()
return err
}
if err := tx.WithContext(ctx).Delete(&models.SchedulerCluster{}, id).Error; err != nil {
tx.Rollback()
return err
}
return tx.Commit().Error
}
func (s *service) UpdateCluster(ctx context.Context, id uint, json types.UpdateClusterRequest) (*types.UpdateClusterResponse, error) {
var (
schedulerClusterConfig map[string]any
err error
)
if json.SchedulerClusterConfig != nil {
schedulerClusterConfig, err = structure.StructToMap(json.SchedulerClusterConfig)
if err != nil {
return nil, err
}
}
var seedPeerClusterConfig map[string]any
if json.SeedPeerClusterConfig != nil {
seedPeerClusterConfig, err = structure.StructToMap(json.SeedPeerClusterConfig)
if err != nil {
return nil, err
}
}
var peerClusterConfig map[string]any
if json.PeerClusterConfig != nil {
peerClusterConfig, err = structure.StructToMap(json.PeerClusterConfig)
if err != nil {
return nil, err
}
}
var scopes map[string]any
if json.Scopes != nil {
scopes, err = structure.StructToMap(json.Scopes)
if err != nil {
return nil, err
}
}
// Update cluster with transaction.
tx := s.db.WithContext(ctx).Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Error; err != nil {
return nil, err
}
schedulerCluster := models.SchedulerCluster{}
if err := tx.WithContext(ctx).Preload("SeedPeerClusters").First(&schedulerCluster, id).Updates(models.SchedulerCluster{
Name: json.Name,
BIO: json.BIO,
Config: schedulerClusterConfig,
ClientConfig: peerClusterConfig,
Scopes: scopes,
}).Error; err != nil {
tx.Rollback()
return nil, err
}
// Updates does not accept bool as false.
// Refer to https://stackoverflow.com/questions/56653423/gorm-doesnt-update-boolean-field-to-false.
if json.IsDefault != schedulerCluster.IsDefault {
if err := tx.WithContext(ctx).First(&models.SchedulerCluster{}, id).Update("is_default", json.IsDefault).Error; err != nil {
tx.Rollback()
return nil, err
}
}
if len(schedulerCluster.SeedPeerClusters) != 1 {
tx.Rollback()
return nil, errors.New("invalid number of the seed peer cluster")
}
seedPeerCluster := models.SeedPeerCluster{}
if err := tx.WithContext(ctx).First(&seedPeerCluster, schedulerCluster.SeedPeerClusters[0].ID).Updates(models.SeedPeerCluster{
Name: json.Name,
BIO: json.BIO,
Config: seedPeerClusterConfig,
}).Error; err != nil {
tx.Rollback()
return nil, err
}
if tx.Commit().Error != nil {
return nil, err
}
return &types.UpdateClusterResponse{
ID: schedulerCluster.ID,
Name: schedulerCluster.Name,
BIO: schedulerCluster.BIO,
Scopes: json.Scopes,
SchedulerClusterID: schedulerCluster.ID,
SeedPeerClusterID: seedPeerCluster.ID,
SchedulerClusterConfig: json.SchedulerClusterConfig,
SeedPeerClusterConfig: json.SeedPeerClusterConfig,
PeerClusterConfig: json.PeerClusterConfig,
CreatedAt: schedulerCluster.CreatedAt,
UpdatedAt: schedulerCluster.UpdatedAt,
IsDefault: json.IsDefault,
}, nil
}
func (s *service) GetCluster(ctx context.Context, id uint) (*types.GetClusterResponse, error) {
schedulerCluster := models.SchedulerCluster{}
if err := s.db.WithContext(ctx).Preload("SeedPeerClusters").First(&schedulerCluster, id).Error; err != nil {
return nil, err
}
if len(schedulerCluster.SeedPeerClusters) != 1 {
return nil, errors.New("invalid number of the seed peer cluster")
}
scopes := &types.SchedulerClusterScopes{}
if err := structure.MapToStruct(schedulerCluster.Scopes, &scopes); err != nil {
return nil, err
}
schedulerClusterConfig := &types.SchedulerClusterConfig{}
if err := structure.MapToStruct(schedulerCluster.ClientConfig, &schedulerClusterConfig); err != nil {
return nil, err
}
seedPeerClusterConfig := &types.SeedPeerClusterConfig{}
if err := structure.MapToStruct(schedulerCluster.SeedPeerClusters[0].Config, &seedPeerClusterConfig); err != nil {
return nil, err
}
peerClusterConfig := &types.SchedulerClusterClientConfig{}
if err := structure.MapToStruct(schedulerCluster.ClientConfig, &peerClusterConfig); err != nil {
return nil, err
}
return &types.GetClusterResponse{
ID: schedulerCluster.ID,
Name: schedulerCluster.Name,
BIO: schedulerCluster.BIO,
Scopes: scopes,
SchedulerClusterID: schedulerCluster.ID,
SeedPeerClusterID: schedulerCluster.SeedPeerClusters[0].ID,
SchedulerClusterConfig: schedulerClusterConfig,
SeedPeerClusterConfig: seedPeerClusterConfig,
PeerClusterConfig: peerClusterConfig,
CreatedAt: schedulerCluster.CreatedAt,
UpdatedAt: schedulerCluster.UpdatedAt,
IsDefault: schedulerCluster.IsDefault,
}, nil
}
func (s *service) GetClusters(ctx context.Context, q types.GetClustersQuery) ([]types.GetClusterResponse, int64, error) {
var count int64
var schedulerClusters []models.SchedulerCluster
if err := s.db.WithContext(ctx).Scopes(models.Paginate(q.Page, q.PerPage)).Where(&models.SchedulerCluster{
Name: q.Name,
}).Preload("SeedPeerClusters").Find(&schedulerClusters).Limit(-1).Offset(-1).Count(&count).Error; err != nil {
return nil, 0, err
}
var resp []types.GetClusterResponse
for _, schedulerCluster := range schedulerClusters {
if len(schedulerCluster.SeedPeerClusters) != 1 {
return nil, 0, errors.New("invalid number of the seed peer cluster")
}
scopes := &types.SchedulerClusterScopes{}
if err := structure.MapToStruct(schedulerCluster.Scopes, &scopes); err != nil {
return nil, 0, err
}
schedulerClusterConfig := &types.SchedulerClusterConfig{}
if err := structure.MapToStruct(schedulerCluster.ClientConfig, &schedulerClusterConfig); err != nil {
return nil, 0, err
}
seedPeerClusterConfig := &types.SeedPeerClusterConfig{}
if err := structure.MapToStruct(schedulerCluster.SeedPeerClusters[0].Config, &seedPeerClusterConfig); err != nil {
return nil, 0, err
}
peerClusterConfig := &types.SchedulerClusterClientConfig{}
if err := structure.MapToStruct(schedulerCluster.ClientConfig, &peerClusterConfig); err != nil {
return nil, 0, err
}
resp = append(resp, types.GetClusterResponse{
ID: schedulerCluster.ID,
Name: schedulerCluster.Name,
BIO: schedulerCluster.BIO,
Scopes: scopes,
SchedulerClusterID: schedulerCluster.ID,
SeedPeerClusterID: schedulerCluster.SeedPeerClusters[0].ID,
SchedulerClusterConfig: schedulerClusterConfig,
SeedPeerClusterConfig: seedPeerClusterConfig,
PeerClusterConfig: peerClusterConfig,
CreatedAt: schedulerCluster.CreatedAt,
UpdatedAt: schedulerCluster.UpdatedAt,
IsDefault: schedulerCluster.IsDefault,
})
}
return resp, count, nil
}

View File

@ -140,6 +140,21 @@ func (mr *MockServiceMockRecorder) CreateBucket(arg0, arg1 interface{}) *gomock.
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateBucket", reflect.TypeOf((*MockService)(nil).CreateBucket), arg0, arg1)
}
// CreateCluster mocks base method.
func (m *MockService) CreateCluster(arg0 context.Context, arg1 types.CreateClusterRequest) (*types.CreateClusterResponse, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateCluster", arg0, arg1)
ret0, _ := ret[0].(*types.CreateClusterResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateCluster indicates an expected call of CreateCluster.
func (mr *MockServiceMockRecorder) CreateCluster(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCluster", reflect.TypeOf((*MockService)(nil).CreateCluster), arg0, arg1)
}
// CreateConfig mocks base method.
func (m *MockService) CreateConfig(arg0 context.Context, arg1 types.CreateConfigRequest) (*models.Config, error) {
m.ctrl.T.Helper()
@ -332,6 +347,20 @@ func (mr *MockServiceMockRecorder) DestroyBucket(arg0, arg1 interface{}) *gomock
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DestroyBucket", reflect.TypeOf((*MockService)(nil).DestroyBucket), arg0, arg1)
}
// DestroyCluster mocks base method.
func (m *MockService) DestroyCluster(arg0 context.Context, arg1 uint) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DestroyCluster", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// DestroyCluster indicates an expected call of DestroyCluster.
func (mr *MockServiceMockRecorder) DestroyCluster(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DestroyCluster", reflect.TypeOf((*MockService)(nil).DestroyCluster), arg0, arg1)
}
// DestroyConfig mocks base method.
func (m *MockService) DestroyConfig(arg0 context.Context, arg1 uint) error {
m.ctrl.T.Helper()
@ -506,6 +535,37 @@ func (mr *MockServiceMockRecorder) GetBuckets(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBuckets", reflect.TypeOf((*MockService)(nil).GetBuckets), arg0)
}
// GetCluster mocks base method.
func (m *MockService) GetCluster(arg0 context.Context, arg1 uint) (*types.GetClusterResponse, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetCluster", arg0, arg1)
ret0, _ := ret[0].(*types.GetClusterResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetCluster indicates an expected call of GetCluster.
func (mr *MockServiceMockRecorder) GetCluster(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCluster", reflect.TypeOf((*MockService)(nil).GetCluster), arg0, arg1)
}
// GetClusters mocks base method.
func (m *MockService) GetClusters(arg0 context.Context, arg1 types.GetClustersQuery) ([]types.GetClusterResponse, int64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetClusters", arg0, arg1)
ret0, _ := ret[0].([]types.GetClusterResponse)
ret1, _ := ret[1].(int64)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// GetClusters indicates an expected call of GetClusters.
func (mr *MockServiceMockRecorder) GetClusters(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusters", reflect.TypeOf((*MockService)(nil).GetClusters), arg0, arg1)
}
// GetConfig mocks base method.
func (m *MockService) GetConfig(arg0 context.Context, arg1 uint) (*models.Config, error) {
m.ctrl.T.Helper()
@ -930,6 +990,21 @@ func (mr *MockServiceMockRecorder) UpdateApplication(arg0, arg1, arg2 interface{
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateApplication", reflect.TypeOf((*MockService)(nil).UpdateApplication), arg0, arg1, arg2)
}
// UpdateCluster mocks base method.
func (m *MockService) UpdateCluster(arg0 context.Context, arg1 uint, arg2 types.UpdateClusterRequest) (*types.UpdateClusterResponse, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateCluster", arg0, arg1, arg2)
ret0, _ := ret[0].(*types.UpdateClusterResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpdateCluster indicates an expected call of UpdateCluster.
func (mr *MockServiceMockRecorder) UpdateCluster(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateCluster", reflect.TypeOf((*MockService)(nil).UpdateCluster), arg0, arg1, arg2)
}
// UpdateConfig mocks base method.
func (m *MockService) UpdateConfig(arg0 context.Context, arg1 uint, arg2 types.UpdateConfigRequest) (*models.Config, error) {
m.ctrl.T.Helper()

View File

@ -32,10 +32,9 @@ func (s *service) CreateSeedPeerCluster(ctx context.Context, json types.CreateSe
}
seedPeerCluster := models.SeedPeerCluster{
Name: json.Name,
BIO: json.BIO,
Config: config,
IsDefault: json.IsDefault,
Name: json.Name,
BIO: json.BIO,
Config: config,
}
if err := s.db.WithContext(ctx).Create(&seedPeerCluster).Error; err != nil {
@ -83,14 +82,6 @@ func (s *service) UpdateSeedPeerCluster(ctx context.Context, id uint, json types
return nil, err
}
// Updates does not accept bool as false.
// Refer to https://stackoverflow.com/questions/56653423/gorm-doesnt-update-boolean-field-to-false.
if json.IsDefault != seedPeerCluster.IsDefault {
if err := s.db.WithContext(ctx).First(&seedPeerCluster, id).Update("is_default", json.IsDefault).Error; err != nil {
return nil, err
}
}
return &seedPeerCluster, nil
}
@ -144,12 +135,7 @@ func (s *service) AddSchedulerClusterToSeedPeerCluster(ctx context.Context, id,
return err
}
seedPeerClusters := []models.SeedPeerCluster{}
if err := s.db.WithContext(ctx).Model(&schedulerCluster).Association("SeedPeerClusters").Find(&seedPeerClusters); err != nil {
return err
}
if err := s.db.WithContext(ctx).Model(&schedulerCluster).Association("SeedPeerClusters").Delete(seedPeerClusters); err != nil {
if err := s.db.WithContext(ctx).Model(&schedulerCluster).Association("SeedPeerClusters").Clear(); err != nil {
return err
}

View File

@ -63,6 +63,12 @@ type Service interface {
GetOauth(context.Context, uint) (*models.Oauth, error)
GetOauths(context.Context, types.GetOauthsQuery) ([]models.Oauth, int64, error)
CreateCluster(context.Context, types.CreateClusterRequest) (*types.CreateClusterResponse, error)
DestroyCluster(context.Context, uint) error
UpdateCluster(context.Context, uint, types.UpdateClusterRequest) (*types.UpdateClusterResponse, error)
GetCluster(context.Context, uint) (*types.GetClusterResponse, error)
GetClusters(context.Context, types.GetClustersQuery) ([]types.GetClusterResponse, int64, error)
CreateSeedPeerCluster(context.Context, types.CreateSeedPeerClusterRequest) (*models.SeedPeerCluster, error)
DestroySeedPeerCluster(context.Context, uint) error
UpdateSeedPeerCluster(context.Context, uint, types.UpdateSeedPeerClusterRequest) (*models.SeedPeerCluster, error)

94
manager/types/cluster.go Normal file
View File

@ -0,0 +1,94 @@
/*
* Copyright 2023 The Dragonfly Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package types
import "time"
type ClusterParams struct {
ID uint `uri:"id" binding:"required"`
}
type CreateClusterRequest struct {
Name string `json:"name" binding:"required"`
BIO string `json:"bio" binding:"omitempty"`
Scopes *SchedulerClusterScopes `json:"scopes" binding:"omitempty"`
SchedulerClusterConfig *SchedulerClusterConfig `json:"scheduler_cluster_config" binding:"required"`
SeedPeerClusterConfig *SeedPeerClusterConfig `json:"seed_peer_cluster_config" binding:"required"`
PeerClusterConfig *SchedulerClusterClientConfig `json:"peer_cluster_config" binding:"required"`
IsDefault bool `json:"is_default" binding:"omitempty"`
}
type CreateClusterResponse struct {
ID uint
Name string
BIO string
Scopes *SchedulerClusterScopes
SchedulerClusterID uint
SeedPeerClusterID uint
SchedulerClusterConfig *SchedulerClusterConfig
SeedPeerClusterConfig *SeedPeerClusterConfig
PeerClusterConfig *SchedulerClusterClientConfig
CreatedAt time.Time
UpdatedAt time.Time
IsDefault bool
}
type UpdateClusterRequest struct {
Name string `json:"name" binding:"omitempty"`
BIO string `json:"bio" binding:"omitempty"`
Scopes *SchedulerClusterScopes `json:"scopes" binding:"omitempty"`
SchedulerClusterConfig *SchedulerClusterConfig `json:"scheduler_cluster_config" binding:"omitempty"`
SeedPeerClusterConfig *SeedPeerClusterConfig `json:"seed_peer_cluster_config" binding:"omitempty"`
PeerClusterConfig *SchedulerClusterClientConfig `json:"peer_cluster_config" binding:"omitempty"`
IsDefault bool `json:"is_default" binding:"omitempty"`
}
type UpdateClusterResponse struct {
ID uint
Name string
BIO string
Scopes *SchedulerClusterScopes
SchedulerClusterID uint
SeedPeerClusterID uint
SchedulerClusterConfig *SchedulerClusterConfig
SeedPeerClusterConfig *SeedPeerClusterConfig
PeerClusterConfig *SchedulerClusterClientConfig
CreatedAt time.Time
UpdatedAt time.Time
IsDefault bool
}
type GetClusterResponse struct {
ID uint
Name string
BIO string
Scopes *SchedulerClusterScopes
SchedulerClusterID uint
SeedPeerClusterID uint
SchedulerClusterConfig *SchedulerClusterConfig
SeedPeerClusterConfig *SeedPeerClusterConfig
PeerClusterConfig *SchedulerClusterClientConfig
CreatedAt time.Time
UpdatedAt time.Time
IsDefault bool
}
type GetClustersQuery struct {
Name string `form:"name" binding:"omitempty"`
Page int `form:"page" binding:"omitempty,gte=1"`
PerPage int `form:"per_page" binding:"omitempty,gte=1,lte=50"`
}

View File

@ -31,17 +31,15 @@ type AddSchedulerClusterToSeedPeerClusterParams struct {
}
type CreateSeedPeerClusterRequest struct {
Name string `json:"name" binding:"required"`
BIO string `json:"bio" binding:"omitempty"`
Config *SeedPeerClusterConfig `json:"config" binding:"required"`
IsDefault bool `json:"is_default" binding:"omitempty"`
Name string `json:"name" binding:"required"`
BIO string `json:"bio" binding:"omitempty"`
Config *SeedPeerClusterConfig `json:"config" binding:"required"`
}
type UpdateSeedPeerClusterRequest struct {
Name string `json:"name" binding:"omitempty"`
BIO string `json:"bio" binding:"omitempty"`
Config *SeedPeerClusterConfig `json:"config" binding:"omitempty"`
IsDefault bool `json:"is_default" binding:"omitempty"`
Name string `json:"name" binding:"omitempty"`
BIO string `json:"bio" binding:"omitempty"`
Config *SeedPeerClusterConfig `json:"config" binding:"omitempty"`
}
type GetSeedPeerClustersQuery struct {

View File

@ -31,5 +31,24 @@ func StructToMap(t any) (map[string]any, error) {
if err := json.Unmarshal(b, &m); err != nil {
return nil, err
}
return m, nil
}
// MapToStruct converts map to struct.
func MapToStruct(m map[string]any, t any) error {
if m == nil {
return nil
}
b, err := json.Marshal(m)
if err != nil {
return err
}
if err := json.Unmarshal(b, t); err != nil {
return err
}
return nil
}

View File

@ -78,3 +78,49 @@ func TestStructToMap(t *testing.T) {
})
}
}
func TestMapToStruct(t *testing.T) {
type person struct {
Name string
Age float64
}
tests := []struct {
name string
m map[string]any
expect func(*testing.T, *person, error)
}{
{
name: "conver struct to map",
m: map[string]any{
"Name": "foo",
"Age": float64(18),
},
expect: func(t *testing.T, s *person, err error) {
assert := assert.New(t)
assert.NoError(err)
assert.Equal(s, &person{
Name: "foo",
Age: 18,
})
},
},
{
name: "conver nil to map",
m: nil,
expect: func(t *testing.T, s *person, err error) {
assert := assert.New(t)
assert.NoError(err)
assert.Equal(s, &person{Name: "", Age: 0})
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
s := &person{}
err := MapToStruct(tc.m, s)
tc.expect(t, s, err)
})
}
}