From 9fc278179801a5ac85b7a847d38be676c419123d Mon Sep 17 00:00:00 2001 From: Stepan Rabotkin Date: Tue, 1 Sep 2026 22:10:59 +0300 Subject: [PATCH 1/5] fix(openapi): require image, not the undefined names, in /image/flatten The schema required "names", which it does not define. The handler reads cfg["image"] (src/cmd/cli_flatten.cpp:141). Signed-off-by: Stepan Rabotkin --- src/cmd/openapi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmd/openapi.json b/src/cmd/openapi.json index d3037a89..8b219963 100644 --- a/src/cmd/openapi.json +++ b/src/cmd/openapi.json @@ -218,7 +218,7 @@ "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", - "required": [ "names" ], + "required": [ "image" ], "properties": { "image": { "type": "string", "description": "Image name to flatten" } } -- 2.47.3 From 50e36a04df4d22ac615c8dceedf8cfde875c30d3 Mon Sep 17 00:00:00 2001 From: Stepan Rabotkin Date: Tue, 1 Sep 2026 22:10:59 +0300 Subject: [PATCH 2/5] fix(openapi): add the required info.title field OpenAPI 3.0 requires info.title. Reuses the text of info.description. Signed-off-by: Stepan Rabotkin --- src/cmd/openapi.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cmd/openapi.json b/src/cmd/openapi.json index 8b219963..c0323673 100644 --- a/src/cmd/openapi.json +++ b/src/cmd/openapi.json @@ -1,6 +1,7 @@ { "openapi": "3.0.0", "info": { + "title": "Vitastor control API", "description": "Vitastor control API", "version": "3.0.2", "license": { -- 2.47.3 From 5f4e394eb045216c8b2a4294eade72aea8f0b6a0 Mon Sep 17 00:00:00 2001 From: Stepan Rabotkin Date: Tue, 1 Sep 2026 22:10:59 +0300 Subject: [PATCH 3/5] fix(openapi): drop the Swagger 2.0 consumes and produces leftovers Removed 48 occurrences. OpenAPI 3.0 has no such fields and the media types are already declared in requestBody.content and responses.*.content. Signed-off-by: Stepan Rabotkin --- src/cmd/openapi.json | 48 -------------------------------------------- 1 file changed, 48 deletions(-) diff --git a/src/cmd/openapi.json b/src/cmd/openapi.json index c0323673..d50c7071 100644 --- a/src/cmd/openapi.json +++ b/src/cmd/openapi.json @@ -12,7 +12,6 @@ "/status": { "get": { "summary": "Show server status", "operationId": "status", - "produces": [ "application/json" ], "parameters": [], "responses": { "200": { "content": { "application/json": { "schema": { @@ -61,7 +60,6 @@ "/image/list": { "get": { "summary": "List images", "operationId": "imageListGet", - "produces": [ "application/json" ], "parameters": [ { "name": "params", "in": "query", @@ -75,8 +73,6 @@ }, "post": { "summary": "List images", "operationId": "imageListPost", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImageListParams" } } } }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImageList" } } } } @@ -85,8 +81,6 @@ "/image/create": { "post": { "summary": "Create an image, snapshot, or a clone", "operationId": "imageCreate", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": [ "image" ], @@ -124,8 +118,6 @@ "/image/modify": { "post": { "summary": "Modify an image", "operationId": "imageModify", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": [ "image" ], @@ -165,8 +157,6 @@ "/image/delete": { "post": { "summary": "Delete image(s)", "operationId": "imageDelete", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "oneOf": [ { "type": "object", @@ -215,8 +205,6 @@ "/image/flatten": { "post": { "summary": "Flatten an image", "operationId": "imageFlatten", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "required": [ "image" ], @@ -235,7 +223,6 @@ "/osd/list": { "get": { "summary": "List OSDs", "operationId": "osdListGet", - "produces": [ "application/json" ], "parameters": [ { "name": "params", "in": "query", @@ -249,8 +236,6 @@ }, "post": { "summary": "List OSDs", "operationId": "osdListPost", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OsdListParams" } } } }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OsdList" } } } } @@ -259,7 +244,6 @@ "/osd/alloc": { "post": { "summary": "Allocate a new OSD number", "operationId": "osdAlloc", - "produces": [ "application/json" ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "integer", @@ -273,8 +257,6 @@ "/osd/delete": { "post": { "summary": "Delete stopped OSD(s) from etcd", "operationId": "osdDelete", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { @@ -323,8 +305,6 @@ "/osd/modify": { "post": { "summary": "Modify OSD reweight, tags or noout flag", "operationId": "osdModify", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { @@ -353,7 +333,6 @@ "/pool/list": { "get": { "summary": "List pools", "operationId": "poolListGet", - "produces": [ "application/json" ], "parameters": [ { "name": "params", "in": "query", @@ -367,8 +346,6 @@ }, "post": { "summary": "List pools", "operationId": "poolListPost", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PoolListParams" } } } }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PoolList" } } } } @@ -377,8 +354,6 @@ "/pool/create": { "post": { "summary": "Create a pool", "operationId": "poolCreate", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/PoolConfig" @@ -399,8 +374,6 @@ "/pool/modify": { "post": { "summary": "Modify a pool", "operationId": "poolModify", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/PoolConfig" @@ -422,8 +395,6 @@ "/pool/delete": { "post": { "summary": "Delete a pool", "operationId": "poolDelete", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { @@ -445,7 +416,6 @@ "/user/list": { "get": { "summary": "List users", "operationId": "userListGet", - "produces": [ "application/json" ], "parameters": [ { "name": "params", "in": "query", @@ -459,8 +429,6 @@ }, "post": { "summary": "List users", "operationId": "userListPost", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserListParams" } } } }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserList" } } } } @@ -469,8 +437,6 @@ "/user/modify": { "post": { "summary": "Modify a user", "operationId": "userModify", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } } }, "responses": { "200": { "content": { "application/json": { "schema": { @@ -484,8 +450,6 @@ "/user/delete": { "post": { "summary": "Delete a user", "operationId": "userDelete", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { @@ -505,7 +469,6 @@ "/pg/list": { "get": { "summary": "List PGs", "operationId": "pgListGet", - "produces": [ "application/json" ], "parameters": [ { "name": "params", "in": "query", @@ -519,8 +482,6 @@ }, "post": { "summary": "List PGs", "operationId": "pgListPost", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PgListParams" } } } }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PgList" } } } } @@ -529,8 +490,6 @@ "/data/delete": { "post": { "summary": "Remove inode data without changing metadata", "operationId": "dataDelete", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { @@ -571,7 +530,6 @@ "/data/describe": { "get": { "summary": "Describe unclean object locations in the cluster", "operationId": "dataDescribeGet", - "produces": [ "application/json" ], "parameters": [ { "name": "params", "in": "query", @@ -585,8 +543,6 @@ }, "post": { "summary": "Describe unclean object locations in the cluster", "operationId": "dataDescribePost", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DataDescribeParams" } } } }, "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DataDescribe" } } } } @@ -595,8 +551,6 @@ "/data/fix": { "post": { "summary": "Fix inconsistent objects in the cluster by deleting some copies", "operationId": "dataFix", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { @@ -635,8 +589,6 @@ "/data/merge": { "post": { "summary": "Merge layer data without changing metadata", "operationId": "dataMerge", - "consumes": [ "application/json" ], - "produces": [ "application/json" ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { -- 2.47.3 From 806ebf5bcd31e147a3582a1fc616e93e81eb14c5 Mon Sep 17 00:00:00 2001 From: Stepan Rabotkin Date: Tue, 1 Sep 2026 22:10:59 +0300 Subject: [PATCH 4/5] fix(openapi): describe every response Added description to the 30 responses that lacked it: the 200 of every operation and the inline 412 of /osd/delete and /data/delete. OpenAPI 3.0 requires it. Signed-off-by: Stepan Rabotkin --- src/cmd/openapi.json | 60 ++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/cmd/openapi.json b/src/cmd/openapi.json index d50c7071..3bfeb703 100644 --- a/src/cmd/openapi.json +++ b/src/cmd/openapi.json @@ -14,7 +14,7 @@ "operationId": "status", "parameters": [], "responses": { - "200": { "content": { "application/json": { "schema": { + "200": { "description": "Cluster status", "content": { "application/json": { "schema": { "type": "object", "properties": { "pool_count": { "type": "integer", "format": "uint64", "description": "Total number of pools" }, @@ -68,14 +68,14 @@ "explode": true } ], "responses": { - "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImageList" } } } } + "200": { "description": "List of images", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImageList" } } } } } }, "post": { "summary": "List images", "operationId": "imageListPost", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImageListParams" } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImageList" } } } } + "200": { "description": "List of images", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImageList" } } } } } } }, "/image/create": { "post": { @@ -108,7 +108,7 @@ } } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Image" } } } }, + "200": { "description": "The created image, snapshot or clone", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Image" } } } }, "400": { "$ref": "#/components/responses/Invalid" }, "403": { "$ref": "#/components/responses/Forbidden" }, "409": { "$ref": "#/components/responses/UpdateConflict" }, @@ -147,7 +147,7 @@ } } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Image" } } } }, + "200": { "description": "The modified image", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Image" } } } }, "400": { "$ref": "#/components/responses/Invalid" }, "403": { "$ref": "#/components/responses/Forbidden" }, "409": { "$ref": "#/components/responses/UpdateConflict" }, @@ -186,7 +186,7 @@ } ] } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { + "200": { "description": "Deleted and rebased images", "content": { "application/json": { "schema": { "type": "object", "properties": { "deleted_ids": { "type": "array", "items": { "type": "integer", "format": "uint64" }, "description": "Full inode ID(s) of deleted images" }, @@ -213,7 +213,7 @@ } } } } }, "responses": { - "200": { "content": { "text/plain": { "schema": { "type": "string", "description": "Empty response" } } } }, + "200": { "description": "Image flattened", "content": { "text/plain": { "schema": { "type": "string", "description": "Empty response" } } } }, "400": { "$ref": "#/components/responses/Invalid" }, "403": { "$ref": "#/components/responses/Forbidden" }, "409": { "$ref": "#/components/responses/UpdateConflict" }, @@ -231,21 +231,21 @@ "explode": true } ], "responses": { - "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OsdList" } } } } + "200": { "description": "List of OSDs", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OsdList" } } } } } }, "post": { "summary": "List OSDs", "operationId": "osdListPost", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OsdListParams" } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OsdList" } } } } + "200": { "description": "List of OSDs", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OsdList" } } } } } } }, "/osd/alloc": { "post": { "summary": "Allocate a new OSD number", "operationId": "osdAlloc", "responses": { - "200": { "content": { "application/json": { "schema": { + "200": { "description": "The allocated OSD number", "content": { "application/json": { "schema": { "type": "integer", "format": "uint64", "description": "New OSD number" @@ -266,7 +266,7 @@ } } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { + "200": { "description": "Deleted OSD numbers", "content": { "application/json": { "schema": { "type": "object", "properties": { "deleted_osds": { @@ -280,7 +280,7 @@ } } } }, "400": { "$ref": "#/components/responses/Invalid" }, "409": { "$ref": "#/components/responses/UpdateConflict" }, - "412": { "content": { "application/json": { "schema": { + "412": { "description": "Precondition failed: removing these OSD(s) would affect some pools", "content": { "application/json": { "schema": { "type": "object", "properties": { "pool_effects": { @@ -315,7 +315,7 @@ } } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { + "200": { "description": "OSD settings after modification", "content": { "application/json": { "schema": { "type": "object", "properties": { "reweight": { "type": "number", "minimum": 0, "maximum": 1, "description": "OSD weight" }, @@ -341,14 +341,14 @@ "explode": true } ], "responses": { - "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PoolList" } } } } + "200": { "description": "List of pools", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PoolList" } } } } } }, "post": { "summary": "List pools", "operationId": "poolListPost", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PoolListParams" } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PoolList" } } } } + "200": { "description": "List of pools", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PoolList" } } } } } } }, "/pool/create": { "post": { @@ -363,7 +363,7 @@ } } ] } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { + "200": { "description": "The created pool", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PoolConfig" } } } }, "400": { "$ref": "#/components/responses/Invalid" }, @@ -384,7 +384,7 @@ } } ] } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { + "200": { "description": "The modified pool", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PoolConfig" } } } }, "400": { "$ref": "#/components/responses/Invalid" }, @@ -404,7 +404,7 @@ } } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { + "200": { "description": "Pool deleted", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" } } } } } }, @@ -424,14 +424,14 @@ "explode": true } ], "responses": { - "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserList" } } } } + "200": { "description": "List of users", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserList" } } } } } }, "post": { "summary": "List users", "operationId": "userListPost", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserListParams" } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserList" } } } } + "200": { "description": "List of users", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserList" } } } } } } }, "/user/modify": { "post": { @@ -439,7 +439,7 @@ "operationId": "userModify", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { + "200": { "description": "The modified user", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } } }, "400": { "$ref": "#/components/responses/Invalid" }, @@ -457,7 +457,7 @@ } } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { + "200": { "description": "User deleted", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" } } } } } }, @@ -477,14 +477,14 @@ "explode": true } ], "responses": { - "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PgList" } } } } + "200": { "description": "List of PGs", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PgList" } } } } } }, "post": { "summary": "List PGs", "operationId": "pgListPost", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PgListParams" } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PgList" } } } } + "200": { "description": "List of PGs", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PgList" } } } } } } }, "/data/delete": { "post": { @@ -500,14 +500,14 @@ } } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { + "200": { "description": "Number of removed objects", "content": { "application/json": { "schema": { "type": "object", "properties": { "removed_objects": { "type": "integer", "format": "uint64", "description": "Removed object count" } } } } } }, "400": { "$ref": "#/components/responses/Invalid" }, - "412": { "content": { "application/json": { "schema": { + "412": { "description": "Precondition failed: some data could not be removed", "content": { "application/json": { "schema": { "type": "object", "properties": { "total_objects": { "type": "integer", "format": "uint64", "description": "Total object count" }, @@ -538,14 +538,14 @@ "explode": true } ], "responses": { - "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DataDescribe" } } } } + "200": { "description": "Unclean object locations", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DataDescribe" } } } } } }, "post": { "summary": "Describe unclean object locations in the cluster", "operationId": "dataDescribePost", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DataDescribeParams" } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DataDescribe" } } } } + "200": { "description": "Unclean object locations", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DataDescribe" } } } } } } }, "/data/fix": { "post": { @@ -574,7 +574,7 @@ } } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { + "200": { "description": "The removed object copy", "content": { "application/json": { "schema": { "type": "object", "properties": { "inode": { "type": "integer", "format": "uint64", "description": "Full inode ID with pool" }, @@ -601,7 +601,7 @@ } } } } }, "responses": { - "200": { "content": { "application/json": { "schema": { + "200": { "description": "The merged layer range", "content": { "application/json": { "schema": { "type": "object", "properties": { "from": { "type": "string", "description": "First merged parent layer" }, -- 2.47.3 From dd9b9f926defb7bb3a49c7e71ff2619e5e17316d Mon Sep 17 00:00:00 2001 From: Stepan Rabotkin Date: Tue, 1 Sep 2026 22:23:42 +0300 Subject: [PATCH 5/5] fix(openapi): drop the leftover format and description on two OSD schemas The 200 schemas of /osd/delete and /osd/modify are objects but carried "format": "uint64" and "description": "New OSD number" at schema level, copied from /osd/alloc where the response really is a number. Signed-off-by: Stepan Rabotkin --- src/cmd/openapi.json | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/cmd/openapi.json b/src/cmd/openapi.json index 3bfeb703..b08c6a13 100644 --- a/src/cmd/openapi.json +++ b/src/cmd/openapi.json @@ -274,9 +274,7 @@ "items": { "type": "integer", "format": "uint64" }, "description": "Deleted OSD numbers" } - }, - "format": "uint64", - "description": "New OSD number" + } } } } }, "400": { "$ref": "#/components/responses/Invalid" }, "409": { "$ref": "#/components/responses/UpdateConflict" }, @@ -321,9 +319,7 @@ "reweight": { "type": "number", "minimum": 0, "maximum": 1, "description": "OSD weight" }, "tags": { "type": "array", "items": { "type": "string" }, "description": "OSD tags" }, "noout": { "type": "boolean", "description": "noout flag" } - }, - "format": "uint64", - "description": "New OSD number" + } } } } }, "400": { "$ref": "#/components/responses/Invalid" }, "409": { "$ref": "#/components/responses/UpdateConflict" }, -- 2.47.3