From 7760ca1c92cec7e6a30f56a927509834689cfa57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Budai?= Date: Wed, 13 Oct 2021 15:16:55 +0200 Subject: [PATCH] cloudapi/v2: ensure only one image per a compose in the API spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We've never had the ability to build multiple images per a compose, this commit thus rips out support for this on the API level: image_requests is now image_request and it accepts only one ImageRequest object instead of an array of them. Signed-off-by: Ondřej Budai --- internal/cloudapi/v2/errors.go | 2 - internal/cloudapi/v2/openapi.v2.gen.go | 120 +++--- internal/cloudapi/v2/openapi.v2.yml | 8 +- internal/cloudapi/v2/v2.go | 482 ++++++++++++------------- internal/cloudapi/v2/v2_test.go | 52 +-- 5 files changed, 327 insertions(+), 337 deletions(-) diff --git a/internal/cloudapi/v2/errors.go b/internal/cloudapi/v2/errors.go index 5a3c7f460..fa73fe3f0 100644 --- a/internal/cloudapi/v2/errors.go +++ b/internal/cloudapi/v2/errors.go @@ -24,7 +24,6 @@ const ( ErrorInvalidOSTreeRef ServiceErrorCode = 9 ErrorInvalidOSTreeRepo ServiceErrorCode = 10 ErrorFailedToMakeManifest ServiceErrorCode = 11 - ErrorMultiImageCompose ServiceErrorCode = 13 ErrorInvalidComposeId ServiceErrorCode = 14 ErrorComposeNotFound ServiceErrorCode = 15 ErrorInvalidErrorId ServiceErrorCode = 16 @@ -84,7 +83,6 @@ func getServiceErrors() serviceErrors { serviceError{ErrorInvalidOSTreeRef, http.StatusBadRequest, "Invalid OSTree ref"}, serviceError{ErrorInvalidOSTreeRepo, http.StatusBadRequest, "Error resolving OSTree repo"}, serviceError{ErrorFailedToMakeManifest, http.StatusBadRequest, "Failed to get manifest"}, - serviceError{ErrorMultiImageCompose, http.StatusBadRequest, "Only single-image composes are currently supported"}, serviceError{ErrorInvalidComposeId, http.StatusBadRequest, "Invalid format for compose id"}, serviceError{ErrorComposeNotFound, http.StatusNotFound, "Compose with given id not found"}, serviceError{ErrorInvalidErrorId, http.StatusBadRequest, "Invalid format for error id, it should be an integer as a string"}, diff --git a/internal/cloudapi/v2/openapi.v2.gen.go b/internal/cloudapi/v2/openapi.v2.gen.go index a19dfa2ad..07225e93c 100644 --- a/internal/cloudapi/v2/openapi.v2.gen.go +++ b/internal/cloudapi/v2/openapi.v2.gen.go @@ -96,7 +96,7 @@ type ComposeRequest struct { // Embedded fields due to inline allOf schema Customizations *Customizations `json:"customizations,omitempty"` Distribution string `json:"distribution"` - ImageRequests []ImageRequest `json:"image_requests"` + ImageRequest ImageRequest `json:"image_request"` } // ComposeStatus defines model for ComposeStatus. @@ -478,65 +478,65 @@ func RegisterHandlers(router EchoRouter, si ServerInterface) { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/+xbe2/buLL/KoTOBbKLK/ntNDFQ7EnTnJ7sbZsiTndxbxMEtDS2uJVIlaTiOIW/+wUf", - "kvWgY+dsdhcL5J/WNsl5/DgznBky372QpRmjQKXwJt+9DHOcggRuvy1A/R+BCDnJJGHUm3if8AIQoRHc", - "e74H9zjNEqhNv8NJDt7E63vrte8RteZbDnzl+R7FqRrRM31PhDGkWC2Rq0z9LiQndKGXCfLg4P0xT2fA", - "EZsjIiEViFAEOIyRJViVpiBQStPrbZVHz31MnnUxqEmf/Do9Ox18zhKGowstmtGfswy4JIY/h4WW+Xsh", - "lTfxIA+WIGTQ9/wmC98TMeZwuyQyvsVhyHK7JeXqL15/MByND18dHff6A+/G9zQGDnFL4phzvNK0Kc5E", - "zOStUbgqU7oKitG2VGvf4/AtJxwiJYDVyS3rTbmazX6DUCq+VaSmEsvcARROSV0inJKgFx4Ne6+Oh69e", - "jcfH42g0cyH2RIgbyii+JY0twk+Hz7vLbjx3MN8GXM4Tt+9UWahJTvoPOYcdypEUL6A0mYYn4hSUH8oY", - "UK7JQIT0gg46lyjNhUQzQDkl33IVLvTEBbkDijgIlvMQ0IKzPOtc0/M5UkwQEYilREqI0JyzVC9RuoCQ", - "PsKIYxqxFDEKaIYFRIhRhNHnz+dvERHXdAEUOJYQda7pJhYYC9eCuUwoYSGWdgfrCr63I2gZAwcti6aC", - "RMzyJNLKFXpjGiG1l0IC1/z/zZZIMpQQIRFOElSwEZNrGkuZiUm3G7FQdFIScibYXHZClnaBBrnohgnp", - "YrU9XetbP90RWL7WPwVhQoIESxDyH/ihcL5bxei2ZHLQAEBZI+Rqa91eZLbjVm/H4ztd37o9oGnuxRXL", - "Q0wvLZl3mqMrFuazUoRbErWFOn+rRKpO+w+EGcE4OpoNwgDPBqNgNOoPg+NeOA4O+4Nh7xCOescwcEkn", - "gWIqH5FLCWEm7SeVNZc5oREisvAW7aLoE+MSJ/vYTWEzktxBEBEOoWR81Z3nNMIpUIkT0RoNYrYMJAsU", - "68CI3ABpHL6C+Xh2GPTD4TwYRbgX4MPBIOjNeoe9wfA4ehW92hnoNoi197ZlgRWv3BG5tkXGeuDaJxI0", - "5K0QcIlwqpImAefaAHCSXMy9yZfv3n9xmHsT7x/dTVLVtWlD90IvvoQ5cKAheGu/JXRUF7Y/GII67gM4", - "Op4F/UE0DPBofBiMBoeH4/Fo1Ov1ep7vzRlPsfQmXp5rMHcoFjkUutmo9AEkjrDEz6kYE5ID3IYsTYl0", - "uswPMRbxj4XnzHKSSGSnO9wvw+FXvDC0m6mpHjFxl9AwySNCF+jj2S+XJ14lX3pMH0ujBKKVTa0fw+/S", - "HFfPCV+YC8lS8oDLU/oxeqf12Wvfi4iCbpbLVqLCY0iCIxfExv7t2Wuscx/sztWyAgIXcFVTrMnVYvmo", - "lW78/tmcT3MXJd2dWloR3IHD0tmiQ2s/66JUzbuS/GdMyAUH8cTEvxJsd+k1rc5d+14ubB24195/FsD3", - "cRbfO+Oc8Wf1EBaBEw01CVdyCEfug4UB5vGwqTmU0xuE3dustXxPnhIL9GyHbRbw77UPBt1dzmdIuSV/", - "d/ppR2Ewy8OvILenipgiuCdCquA7vTr5+Pbk8i2aSsZVcA4TLAR6o0l0mom6/RJYDltDk7souYrBVBKS", - "oVwAmjNuU6+McWkTdV27RkiFklwCOqMLQm121rmmV2Wmpgk16hhV8drs7N3pJ5RxpmDz0TImYazql1xA", - "dE0LvhdTS8vkepq9kaWDVNHDJBIZhGROlGy2wLmmB6EJczzAGQmu815vGKrTXX+CA2TAKNghLCr5pZL6", - "KQXQpoBtQ6lUNOOVNLbUaUmSREFTgitZFV9VwVk8dQumhBKr7yTS1ItEr4OmAKjIcMOE5VFnwdgiAZ3f", - "CmM6OvXtlmWOrRyrIPpaxDRPJAms5MV0FCZMgJBKTDXJpJzX9Adb0RTmaQyzXPajgjmMmQCKcC5ZiiUJ", - "cZKsmiBD/oSmTqPUVOkKmxe4aL1RMV3Jq6nULdllvto8O9f0DIdxYSQa9ZBRiYmqlgukeJFsWTZISd5B", - "v2gJTEopEOYwuaYIBehAnQWT75BikpBofTBBJxTpbwhHEQehTBBLxCHjIFQ82vAKFQnUUKuD/sU4suj5", - "6AAnJIR/2u9qzw86lrMAfkdCODHrniiDYW1JbOOdrgImY+1t2T9xlomMyc7CLirWVEXSZcpT0bD6Fz0P", - "JVcDgiglVDgxiFiKCZ18N/8rhto90TQnEpD5Ff2QcZJivvqxzTxJDEPdrFGnutl9LO3aJiIb1ztAjKOD", - "hkxur3vcNIkwa0xwUIaKMF1d0wLfujd90cnHpGUVqnys28O+m+f5ntm2Nsye71mAqz8+Ic3a1iW1h5ir", - "gizP2OcrYX3PHke3zUoSixBohKkMZhyTKBj2huP+cGetWCHn76qIa5l/u8XLw5hICGXOG+rcHx3eHo62", - "n/Pm5z3y8atVBrrcMdXmrjUX0ys1S2ucMUEk481s67Hll8WilSvpNqf9Lcv2qtjquVarS12FroZKQ/QW", - "25tiW7aZ2JOrnV/0VcpGwf0I1Oy8qV5RKdVlNYyUodA81dPyMAShlJxjkhgoMqCqutd+RhL70UhmPhcd", - "WfXtxmFhFbupsMJLxWYRZp7v6Y6aikvRAoKyIaG/ESokThLgTtJF3l8H/Cuh7jKkuDmzA4RKWJhqqrjF", - "ao9IJnHiGmogrJn65ZWbuekyi/2tZYDvWQdxXHjM202E7lHXOHJXYePy5q13FW3GjXKvJUFsRWhHDDe4", - "W1BvN8b8AivNwQVKszfkDHROISBjW0aKEO/IzBPAwj0myCKNxtuGKC4C7ZaDyzFwB1yQfUphG3u02Jtl", - "G3F9A0Ipo3LtSrhs15JYgLWOjVGVlUBEOxyiGJs+t8pjgcpuRITsKsM72lieosNEl4lurSnKE5c5piBx", - "QuhXN9eUqBpadOYQMY7tMdhhfNEt1v2kQu9rMx4MB6owGxwqvV+XB9pOETSTxAaKuhClDGq4EwKVTGj+", - "P1mUXx8F6pjDaYUzVv8ejswvWr43WMDFdA9ZeCzSys7PGEsA03Zio6a5/GLa6DI1nCKU5M50S77Cqn37", - "DCEHGaihiqQZFmLJeOQSV231rdNm2iazh/aECrKIG7ftkufgtwDxPcYXmNrmXZ3/oDfqDQfOXEalo8Db", - "Ile7cx2FbkXynelZTRK/iXKNaQWyirqunWw1fhiFPTpXrhcRa3/nmub1+q4lrc7UTh7tW27d4no892a/", - "R/0i0dlf+z1XNEuGJ+herFCqb5K2/ZIrnlO6LYPaJzs3Etj03J39+cWhUk1dq+ta6Rleio4YNvI0l4S6", - "K/2MrWZdQ9brhI0760Hn255mhdCKg0LEAUSD8bh/jE5OTk5Ohx8f8Gk/+b+35/2PV2dj9dv5R/7uf874", - "h/8l//3hw+dl/m98efJzevmenT9czgff3g6it+OH3pur++7hvUuIdjGpiuzdr1S2FH03+lUUhDkncjVV", - "CBqI3gDmBvSZ/vSvIvz+/OtV8chKB1Uzr6Sr4rd5akXonLW7ZFPbxZFM3xDabqpJw02TQXQ830tICNSk", - "TfZ110mGwxjQoNPzbCJanvTL5bKD9bA+Xu1a0X1/fnr2cXoWDDq9TizTRO8hkRq0i+kbzd7eRXGk25UI", - "Z6SSD028gb2AoGpg4g07vU5f5+Ey1jB1bZNXhx8mHN30Uw5YAsKIwhLZ2T7KmEqBCE6SFQoZFbbNzuZI", - "wB1wXGCh4bF9Z/1GzvQ9CUcRqCW2h1q9zDiPvIn3iQlpVfOMHYCQb1i0MjctOgHTHpVlCTE90u5v9hJl", - "84Du0SvK+lXpum5v6uA1r1IypvZCURv0+s/N/TwyjBuQm0EUY4GExFxCpLZx1Os9G397P9PmfU5N/9fu", - "dPHyyfDv//H8T3KpjOQrUEQEIkYaw334x3P/THEuY8bJg7lJyICrvA2VxmkkGf0ZknylbEnLfTAgjP8M", - "E/hM4T6DUEKEQM1BLAxzrtyiGmv1MVZE2S836xvfE3maYlVdFUGjCC5qXRFput9JtNaHmOvu7h1Icy+i", - "z2R9i4fs2Y8Y1wQTUJJZavpuRxtKmOQRCLSMQcbA1WTKDK0CQp1hQARRO9y8A1m/1Pdrj5C/uB9YlYSN", - "sJKhhb4t1I97VYjdvO21L4yq4aX60vfZ39vctGJX77ljV9lLaxlQHZe/LHQVceMlar1Erb2i1lUj8GwN", - "X7oDU/TeHo1jxURDcE4oEXEjegGCexxKpPJN5dSEUcRB5pxChCJQNZBAjFbfIRePnM196SPRrOwRvsSz", - "nfFs89aubVxX1a0s3lWYd+TFVr6EuZcw9/cIc63YpAwaVwxZhTtNXFTiWyvEbJ6WtYKLS7PNlK6+BdrW", - "OKrM09dEf6jrb3RwWbt5wcvmyILx4mZ/jZsZQ//7ORkuDQgnCcqYEGSWQGlNGzfbXRNhappMNCz/CsZI", - "tnm5N1shfXS6HXW/DKCk+3tP/eGffIaXW/nioy8++hQfNWurpLVfli3T7effhZ3ituq6sJac9lZEKFIY", - "2AeOf8fM4VF11uVVo4kz9V43zkhHLRcxsX82hjPS1dVMoBvqwIPi4XH3buA1tfhgHxmyKA/Ny1jDS+cT", - "bVZC4gX8LoZTiReELtpsnkhHY02Lt47e+mb9/wEAAP//rQggIvc+AAA=", + "H4sIAAAAAAAC/+xbe28bOZL/KkTvAZ7BdestxxYwmHUcb9Z7kziwnFncxYZBdZfU3HSTHZJtWQ703Q98", + "dKsf1MM7nlkM4H8SSSSrflWsKlYV6e9eyNKMUaBSeJPvXoY5TkECt98WoP6PQIScZJIw6k28T3gBiNAI", + "Hj3fg0ecZgnUpj/gJAdv4vW99dr3iFrzLQe+8nyP4lSN6Jm+J8IYUqyWyFWmfheSE7rQywR5cvD+mKcz", + "4IjNEZGQCkQoAhzGyBKsoikIlGh6va149NxdeNbFoCZ99s/pxfngc5YwHF1paEZ+zjLgkhj+HBYa8/cC", + "lTfxIA+WIGTQ9/wmC98TMeZwvyQyvsdhyHK7JeXqL15/MByNj9+cnPb6A+/O97QOHHBL4phzvNK0Kc5E", + "zOS9EbiKKV0FxWgb1dr3OHzLCYdIAbAyubHelavZ7F8QSsW3qqmpxDJ3KAqnpI4IpyTohSfD3pvT4Zs3", + "4/HpOBrNXBp7poobwii+JY0t4KfDl91ltz73MN+muJwnbt+pslCTnPSfcg57hCMpXkBpMg1PxCkoP5Qx", + "oFyTgQjpBR10KVGaC4lmgHJKvuUqXOiJC/IAFHEQLOchoAVneda5pZdzpJggIhBLiZQQoTlnqV6iZAEh", + "fYQRxzRiKWIU0AwLiBCjCKPPny/fISJu6QIocCwh6tzSTSwwFq6BuUwoYSGWdgfrAv5iR9AyBg4ai6aC", + "RMzyJNLCFXJjGiG1l0IC1/z/zpZIMpQQIRFOElSwEZNbGkuZiUm3G7FQdFIScibYXHZClnaBBrnohgnp", + "YrU9XetbPz8QWP6kfwrChAQJliDkX/BT4Xz3itF9yeSooQBljZCrrXV7kdmOe70du3e6vnUHqKa5Fzcs", + "DzG9tmTea46uWJjPSgj3JGqDunynIFWn/RtgRjCOTmaDMMCzwSgYjfrD4LQXjoPj/mDYO4aT3ikMXOgk", + "UEzlDlwKhJl0GCprLnNCI0Rk4S3aRdEnxiVODrGbwmYkeYAgIhxCyfiqO89phFOgEieiNRrEbBlIFijW", + "gYHcUNI4fAPz8ew46IfDeTCKcC/Ax4NB0Jv1jnuD4Wn0JnqzN9BtNNbe25YFVrxyT+TaFhnrgeuQSNDA", + "WyHggnCukiYBl9oAcJJczb3Jl+/ef3GYexPvL91NUtW1aUP3Si++hjlwoCF4a78FOqqD7Q+GoI77AE5O", + "Z0F/EA0DPBofB6PB8fF4PBr1er2e53tzxlMsvYmX51qZewSLHALdbUT6ABJHWOKXFIwJyQHuQ5amRDpd", + "5ocYi/jHwnNmOUkkstMd7pfh8CteGNrN1FSPmLhLaJjkEaEL9PHi1+szr5Iv7ZLH0igV0cqm1rv0d22O", + "q5dUX5gLyVLyhMtTehe98/rste9FRKlulstWosJjSIITl4qN/fONMLtYXqrJheBNg6txbxLeaYob534x", + "D9PMRUl3r1AWgjs6WDpbZGhtWh1K1YYrGX7GhFxwEM/M7isRdZ9c0+rcte/lwhZ7BznHZwH8EI/wvQvO", + "GX9RN2AROLWhJuFKouBIcLAwitkdGzWHcnqDsHubtZS/kOc4vJ7tsM1C/Qftg9GuayNqdqpJuZG/P/+0", + "J/uf5eFXkNvzQUwRPBIhVYSd3px9fHd2/Q5NJeMqAocJFgK91SQ6zWzcfgksh63xx1153MRgygXJUC4A", + "zRm3+VXGuLTZuC5QI6RCSS4BXdAFoTYF69zSmzId04QaxYoqa20K9v78E8o4U2rz0TImYayKlFxAdEsL", + "vldTS8skdJq9wdJBqrJhEokMQjInCputYm7pUWjCHA9wRoLbvNcbhuoI15/gCBllFOwQFpUkUqF+TpWz", + "qVLbqlQimvFKrlrKtCRJolRTKleyqn5VmWb1qfsspSqx+k4iTb3I5jpoCoCKNDZMWB51FowtEtBJrDCm", + "o/PbblnL2PKwqkRfQ0zzRJLAIi+mozBhAoRUMNUkk1fe0h9s2VKYpzHMctmPSs1hzARQhHPJUixJiJNk", + "1VQy5M/o3DTqSZWTsHmhFy03KqYrvJpK3ZJd5qvNs3NLL3AYF0aitR4yKjFRJXGhKV5kVJYNUsg76FeN", + "wOSNAmEOk1uKUICO1Fkw+Q4pJgmJ1kcTdEaR/oZwFHEQygSxRBwyDkLFow2vUJFADbE66G+MI6s9Hx3h", + "hITwV/td7flRx3IWwB9ICGdm3TMxGNaWxDbe6SpgMtbelv0VZ5nImOws7KJiTRWSrkWeqw0rf9HYULga", + "KohSQoVTBxFLMaGT7+Z/xVC7J5rmRAIyv6IfMk5SzFc/tpkniWGoOzLqVDe7j6Vd29TIxvWOEOPoqIHJ", + "7XW7TZMIs8YEB2WoCNPVLS30W/emLzr5mLSsQtWIdXs4dPM83zPb1laz53tWwdUfn5FmbWuF2kPMVSaW", + "Z+zL1am+Z4+j+2a5iEUINMJUBjOOSRQMe8Nxf7i3IKyQ8/eVvbVEv93H5WFMJIQy5w1xHk+O749H2895", + "8/MB+fjNKgNd05iSct+aq+mNmqUlzpggkvFmtrVr+XWxaOVKus1pf8+yg8qyeq7VakVXVVfTSgN6i+1d", + "sS3bTOzZ1c6v+r5kI+BhBGp23hSvqJTqWA0jZSg0T/W0PAxBKCHnmCRGFRlQVcJrPyOJ/WiQmc9F21V9", + "u3NYWMVuKqzwUrFZhJnne7ptpuJStICg7Drob4QKiZMEuJN0kffXFf6VUHcZUlyP2QFCJSxMNVVcVbVH", + "JJM4cQ01NKyZ+uW9mrnOMov9rWWA71kHcdxqzNudgu5J1zhyV+nG5c1bLyTajBvlXgtBbCG0I4ZbuVu0", + "3u5++YWuNAeXUpoNIGegc4KAjG0ZKUK8IzNPAAv3mCCLNBpvG6K4CLRbDi7HwANwQQ4phW3s0bA3yzZw", + "faOEEqNy7Uq4bNeSWIC1jo1RlZVARDscohibZrbKY4HKbkSE7CrDO9lYnqLDRJeJbq3zyROXOaYgcULo", + "VzfXlKgaWnTmEDGO7THYYXzRLdb9rELvT2Y8GA5UYTY4VnL/VB5oeyFoJokNFHUQJQY13AmBSiY0/5+t", + "ln86CdQxh9MKZ6z+PR6ZXzS+t1jA1fQALDwWaWXnZ4wlgGk7sVHTXH4xbXSZGk4RSvJguiVfYdW+YoaQ", + "gwzUUAVphoVYMh654KqtvnfaTNtkDpCeUEEWceNKXfIc/JZCfI/xBaa2eVfnP+iNesOBM5dR6SjwNuRq", + "d66jtFtBvjc9qyHxm1quMa2orCKuaydbjR9G4YDOlevZw9rfu6Z5h75vSasztZdH+ypbt7h2597st4hf", + "JDqHS3/gimbJ8AzZixVK9E3SdlhyxXNKt2VQh2TnBoFNz93Zn18cKtXUtbqulZ7hpeiIYSNPcyHUXekX", + "bDXrGrJeJ2zcWQ86H/A0K4RWHBQiDiAajMf9U3R2dnZ2Pvz4hM/7yf+9u+x/vLkYq98uP/L3/3PBP/wv", + "+e8PHz4v87/j67N/pNe/sMun6/ng27tB9G781Ht789g9fnSBaBeTqsje/xRlS9F3p58+QZhzIldTpUGj", + "oreAuVH6TH/6WxF+//HPm+IllQ6qZl5JV8Vv856K0Dlrd8mmtosjmb4GtN1Uk4abJoPoeL6XkBCoSZvs", + "E66zDIcxoEGn59lEtDzpl8tlB+thfbzataL7y+X5xcfpRTDo9DqxTBO9h0RqpV1N32r29i6KI92uRDgj", + "lXxo4g3sBQRVAxNv2Ol1+joPl7FWU9c2eXX4YcLRTT/ngCUgjCgskZ3to4ypFIjgJFmhkFFh2+xsjgQ8", + "AMeFLrR6bN9ZP4QzfU/CUQRqie2hVi8zLiNv4n1iQlrRPGMHIORbFq3MTYtOwLRHZVlCTI+0+y97ibJ5", + "JbfzHrJ+H7qu25s6eM3Tk4ypvVDUBr3+S3O/jAzjhsrNIIqxQEJiLiFS2zjq9V6Mv72fafO+pKb/a3e6", + "eN5k+Pd/f/5nuVRG8hUoIgIRg8ZwH/7+3D9TnMuYcfJkbhIy4CpvQ6VxGiSjPwLJV8qWtNwHo4TxH2EC", + "nyk8ZhBKiBCoOYiFYc6VW1RjrT7Giij75W5953siT1OsqqsiaBTBRa0rIk33O4nW+hBz3d29B2nuRfSZ", + "rG/xkD37EeOaYAIKmaWm73a0oYRJHoFAyxhkDFxNpszQKlSoMwyIIGqHm/cg65f6fu2l8Rf3K6qSsAEr", + "GVro20L9gleF2M0DXvuMqBpeqs95X/xRzV0rdvVeOnaVvbSWAdX18h8LXUXceI1ar1HroKh10wg8W8OX", + "7sAUvbedcayYaAjOCSUibkQvQPCIQ4lUvqmcmjCKOMicU4hQBKoGEojR6mPj4iWzuS/dEc3KHuFrPNsb", + "zzYP6trGdVPdyuJdhXksXmzla5h7DXN/jjDXik3KoHHFkFW408RFJb61QszmaVkruLgk20zp6lugbY2j", + "yjx9TfS7uv5GBpe1m2e6bI6sMl7d7D/jZsbQ/3xOhksDwkmCMiYEmSVQWtPGzfbXRJiaJhMNyz91Mcg2", + "L/dmK6SPTrejHpYBlHR/66k//IPP8HIrX3301Uef46NmbZW09suyZbr9/LuyU9xWXQdryWlvRYQipQP7", + "wPHPmDnsFGddXjWaOFPvdeOMdNRyERP7t2E4I11dzQS6oQ48KB4edx8GXlOKD/aRIYvy0LyMNbx0PtFm", + "JSRewG9iOJV4QeiizeaZdLSuafHW0Vvfrf8/AAD//8f1mrXcPgAA", } // GetSwagger returns the Swagger specification corresponding to the generated code diff --git a/internal/cloudapi/v2/openapi.v2.yml b/internal/cloudapi/v2/openapi.v2.yml index a01574c26..64f7f2d84 100644 --- a/internal/cloudapi/v2/openapi.v2.yml +++ b/internal/cloudapi/v2/openapi.v2.yml @@ -474,15 +474,13 @@ components: - type: object required: - distribution - - image_requests + - image_request properties: distribution: type: string example: 'rhel-8' - image_requests: - type: array - items: - $ref: '#/components/schemas/ImageRequest' + image_request: + $ref: '#/components/schemas/ImageRequest' customizations: $ref: '#/components/schemas/Customizations' ImageRequest: diff --git a/internal/cloudapi/v2/v2.go b/internal/cloudapi/v2/v2.go index 92bedd573..bb7ce6e98 100644 --- a/internal/cloudapi/v2/v2.go +++ b/internal/cloudapi/v2/v2.go @@ -147,10 +147,6 @@ func (h *apiHandlers) PostCompose(ctx echo.Context) error { return HTTPError(ErrorUnsupportedDistribution) } - if len(request.ImageRequests) != 1 { - return HTTPError(ErrorMultiImageCompose) - } - var bp = blueprint.Blueprint{} err = bp.Initialize() if err != nil { @@ -164,13 +160,12 @@ func (h *apiHandlers) PostCompose(ctx echo.Context) error { } } - type imageRequest struct { + var imageRequest struct { manifest distro.Manifest arch string exports []string target *target.Target } - imageRequests := make([]imageRequest, len(request.ImageRequests)) // use the same seed for all images so we get the same IDs bigSeed, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64)) @@ -179,249 +174,248 @@ func (h *apiHandlers) PostCompose(ctx echo.Context) error { } manifestSeed := bigSeed.Int64() - for i, ir := range request.ImageRequests { - arch, err := distribution.GetArch(ir.Architecture) - if err != nil { - return HTTPError(ErrorUnsupportedArchitecture) - } - imageType, err := arch.GetImageType(imageTypeFromApiImageType(ir.ImageType)) - if err != nil { - return HTTPError(ErrorUnsupportedImageType) - } - repositories := make([]rpmmd.RepoConfig, len(ir.Repositories)) - for j, repo := range ir.Repositories { - repositories[j].RHSM = repo.Rhsm + ir := request.ImageRequest + arch, err := distribution.GetArch(ir.Architecture) + if err != nil { + return HTTPError(ErrorUnsupportedArchitecture) + } + imageType, err := arch.GetImageType(imageTypeFromApiImageType(ir.ImageType)) + if err != nil { + return HTTPError(ErrorUnsupportedImageType) + } + repositories := make([]rpmmd.RepoConfig, len(ir.Repositories)) + for j, repo := range ir.Repositories { + repositories[j].RHSM = repo.Rhsm - if repo.Baseurl != nil { - repositories[j].BaseURL = *repo.Baseurl - } else if repo.Mirrorlist != nil { - repositories[j].MirrorList = *repo.Mirrorlist - } else if repo.Metalink != nil { - repositories[j].Metalink = *repo.Metalink - } else { - return HTTPError(ErrorInvalidRepository) - } - } - - packageSets := imageType.PackageSets(bp) - depsolveJobID, err := h.server.workers.EnqueueDepsolve(&worker.DepsolveJob{ - PackageSets: packageSets, - Repos: repositories, - ModulePlatformID: distribution.ModulePlatformID(), - Arch: arch.Name(), - Releasever: distribution.Releasever(), - }) - if err != nil { - return HTTPErrorWithInternal(ErrorEnqueueingJob, err) - } - - var depsolveResults worker.DepsolveJobResult - for { - status, _, err := h.server.workers.JobStatus(depsolveJobID, &depsolveResults) - if err != nil { - return HTTPErrorWithInternal(ErrorGettingDepsolveJobStatus, err) - } - if status.Canceled { - return HTTPErrorWithInternal(ErrorDepsolveJobCanceled, err) - } - if !status.Finished.IsZero() { - break - } - time.Sleep(50 * time.Millisecond) - } - - if depsolveResults.Error != "" { - if depsolveResults.ErrorType == worker.DepsolveErrorType { - return HTTPError(ErrorDNFError) - } - return HTTPErrorWithInternal(ErrorFailedToDepsolve, errors.New(depsolveResults.Error)) - } - - pkgSpecSets := depsolveResults.PackageSpecs - - imageOptions := distro.ImageOptions{Size: imageType.Size(0)} - if request.Customizations != nil && request.Customizations.Subscription != nil { - imageOptions.Subscription = &distro.SubscriptionImageOptions{ - Organization: request.Customizations.Subscription.Organization, - ActivationKey: request.Customizations.Subscription.ActivationKey, - ServerUrl: request.Customizations.Subscription.ServerUrl, - BaseUrl: request.Customizations.Subscription.BaseUrl, - Insights: request.Customizations.Subscription.Insights, - } - } - - // set default ostree ref, if one not provided - ostreeOptions := ir.Ostree - if ostreeOptions == nil || ostreeOptions.Ref == nil { - imageOptions.OSTree = distro.OSTreeImageOptions{Ref: imageType.OSTreeRef()} - } else if !ostree.VerifyRef(*ostreeOptions.Ref) { - return HTTPError(ErrorInvalidOSTreeRef) + if repo.Baseurl != nil { + repositories[j].BaseURL = *repo.Baseurl + } else if repo.Mirrorlist != nil { + repositories[j].MirrorList = *repo.Mirrorlist + } else if repo.Metalink != nil { + repositories[j].Metalink = *repo.Metalink } else { - imageOptions.OSTree = distro.OSTreeImageOptions{Ref: *ostreeOptions.Ref} - } - - var parent string - if ostreeOptions != nil && ostreeOptions.Url != nil { - imageOptions.OSTree.URL = *ostreeOptions.Url - parent, err = ostree.ResolveRef(imageOptions.OSTree.URL, imageOptions.OSTree.Ref) - if err != nil { - return HTTPErrorWithInternal(ErrorInvalidOSTreeRepo, err) - } - imageOptions.OSTree.Parent = parent - } - - // Set the blueprint customisation to take care of the user - var blueprintCustoms *blueprint.Customizations - if request.Customizations != nil && request.Customizations.Users != nil { - var userCustomizations []blueprint.UserCustomization - for _, user := range *request.Customizations.Users { - var groups []string - if user.Groups != nil { - groups = *user.Groups - } else { - groups = nil - } - userCustomizations = append(userCustomizations, - blueprint.UserCustomization{ - Name: user.Name, - Key: user.Key, - Groups: groups, - }, - ) - } - blueprintCustoms = &blueprint.Customizations{ - User: userCustomizations, - } - } - - manifest, err := imageType.Manifest(blueprintCustoms, imageOptions, repositories, pkgSpecSets, manifestSeed) - if err != nil { - return HTTPErrorWithInternal(ErrorFailedToMakeManifest, err) - } - - imageRequests[i].manifest = manifest - imageRequests[i].arch = arch.Name() - imageRequests[i].exports = imageType.Exports() - - /* oneOf is not supported by the openapi generator so marshal and unmarshal the uploadrequest based on the type */ - switch ir.ImageType { - case ImageTypes_aws: - var awsUploadOptions AWSEC2UploadOptions - jsonUploadOptions, err := json.Marshal(ir.UploadOptions) - if err != nil { - return HTTPError(ErrorJSONMarshallingError) - } - err = json.Unmarshal(jsonUploadOptions, &awsUploadOptions) - if err != nil { - return HTTPError(ErrorJSONUnMarshallingError) - } - - key := fmt.Sprintf("composer-api-%s", uuid.New().String()) - t := target.NewAWSTarget(&target.AWSTargetOptions{ - Filename: imageType.Filename(), - Region: awsUploadOptions.Region, - Bucket: h.server.awsBucket, - Key: key, - ShareWithAccounts: awsUploadOptions.ShareWithAccounts, - }) - if awsUploadOptions.SnapshotName != nil { - t.ImageName = *awsUploadOptions.SnapshotName - } else { - t.ImageName = key - } - - imageRequests[i].target = t - case ImageTypes_edge_installer: - fallthrough - case ImageTypes_edge_commit: - var awsS3UploadOptions AWSS3UploadOptions - jsonUploadOptions, err := json.Marshal(ir.UploadOptions) - if err != nil { - return HTTPError(ErrorJSONMarshallingError) - } - err = json.Unmarshal(jsonUploadOptions, &awsS3UploadOptions) - if err != nil { - return HTTPError(ErrorJSONUnMarshallingError) - } - - key := fmt.Sprintf("composer-api-%s", uuid.New().String()) - t := target.NewAWSS3Target(&target.AWSS3TargetOptions{ - Filename: imageType.Filename(), - Region: awsS3UploadOptions.Region, - Bucket: h.server.awsBucket, - Key: key, - }) - t.ImageName = key - - imageRequests[i].target = t - case ImageTypes_gcp: - var gcpUploadOptions GCPUploadOptions - jsonUploadOptions, err := json.Marshal(ir.UploadOptions) - if err != nil { - return HTTPError(ErrorJSONMarshallingError) - } - err = json.Unmarshal(jsonUploadOptions, &gcpUploadOptions) - if err != nil { - return HTTPError(ErrorJSONUnMarshallingError) - } - - var share []string - if gcpUploadOptions.ShareWithAccounts != nil { - share = *gcpUploadOptions.ShareWithAccounts - } - - object := fmt.Sprintf("composer-api-%s", uuid.New().String()) - t := target.NewGCPTarget(&target.GCPTargetOptions{ - Filename: imageType.Filename(), - Region: gcpUploadOptions.Region, - Os: "", // not exposed in cloudapi for now - Bucket: gcpUploadOptions.Bucket, - Object: object, - ShareWithAccounts: share, - }) - // Import will fail if an image with this name already exists - if gcpUploadOptions.ImageName != nil { - t.ImageName = *gcpUploadOptions.ImageName - } else { - t.ImageName = object - } - - imageRequests[i].target = t - case ImageTypes_azure: - var azureUploadOptions AzureUploadOptions - jsonUploadOptions, err := json.Marshal(ir.UploadOptions) - if err != nil { - return HTTPError(ErrorJSONMarshallingError) - } - err = json.Unmarshal(jsonUploadOptions, &azureUploadOptions) - if err != nil { - return HTTPError(ErrorJSONUnMarshallingError) - } - t := target.NewAzureImageTarget(&target.AzureImageTargetOptions{ - Filename: imageType.Filename(), - TenantID: azureUploadOptions.TenantId, - Location: azureUploadOptions.Location, - SubscriptionID: azureUploadOptions.SubscriptionId, - ResourceGroup: azureUploadOptions.ResourceGroup, - }) - - if azureUploadOptions.ImageName != nil { - t.ImageName = *azureUploadOptions.ImageName - } else { - // if ImageName wasn't given, generate a random one - t.ImageName = fmt.Sprintf("composer-api-%s", uuid.New().String()) - } - - imageRequests[i].target = t - default: - return HTTPError(ErrorUnsupportedImageType) + return HTTPError(ErrorInvalidRepository) } } - id, err := h.server.workers.EnqueueOSBuild(imageRequests[0].arch, &worker.OSBuildJob{ - Manifest: imageRequests[0].manifest, - Targets: []*target.Target{imageRequests[0].target}, - Exports: imageRequests[0].exports, + packageSets := imageType.PackageSets(bp) + depsolveJobID, err := h.server.workers.EnqueueDepsolve(&worker.DepsolveJob{ + PackageSets: packageSets, + Repos: repositories, + ModulePlatformID: distribution.ModulePlatformID(), + Arch: arch.Name(), + Releasever: distribution.Releasever(), + }) + if err != nil { + return HTTPErrorWithInternal(ErrorEnqueueingJob, err) + } + + var depsolveResults worker.DepsolveJobResult + for { + status, _, err := h.server.workers.JobStatus(depsolveJobID, &depsolveResults) + if err != nil { + return HTTPErrorWithInternal(ErrorGettingDepsolveJobStatus, err) + } + if status.Canceled { + return HTTPErrorWithInternal(ErrorDepsolveJobCanceled, err) + } + if !status.Finished.IsZero() { + break + } + time.Sleep(50 * time.Millisecond) + } + + if depsolveResults.Error != "" { + if depsolveResults.ErrorType == worker.DepsolveErrorType { + return HTTPError(ErrorDNFError) + } + return HTTPErrorWithInternal(ErrorFailedToDepsolve, errors.New(depsolveResults.Error)) + } + + pkgSpecSets := depsolveResults.PackageSpecs + + imageOptions := distro.ImageOptions{Size: imageType.Size(0)} + if request.Customizations != nil && request.Customizations.Subscription != nil { + imageOptions.Subscription = &distro.SubscriptionImageOptions{ + Organization: request.Customizations.Subscription.Organization, + ActivationKey: request.Customizations.Subscription.ActivationKey, + ServerUrl: request.Customizations.Subscription.ServerUrl, + BaseUrl: request.Customizations.Subscription.BaseUrl, + Insights: request.Customizations.Subscription.Insights, + } + } + + // set default ostree ref, if one not provided + ostreeOptions := ir.Ostree + if ostreeOptions == nil || ostreeOptions.Ref == nil { + imageOptions.OSTree = distro.OSTreeImageOptions{Ref: imageType.OSTreeRef()} + } else if !ostree.VerifyRef(*ostreeOptions.Ref) { + return HTTPError(ErrorInvalidOSTreeRef) + } else { + imageOptions.OSTree = distro.OSTreeImageOptions{Ref: *ostreeOptions.Ref} + } + + var parent string + if ostreeOptions != nil && ostreeOptions.Url != nil { + imageOptions.OSTree.URL = *ostreeOptions.Url + parent, err = ostree.ResolveRef(imageOptions.OSTree.URL, imageOptions.OSTree.Ref) + if err != nil { + return HTTPErrorWithInternal(ErrorInvalidOSTreeRepo, err) + } + imageOptions.OSTree.Parent = parent + } + + // Set the blueprint customisation to take care of the user + var blueprintCustoms *blueprint.Customizations + if request.Customizations != nil && request.Customizations.Users != nil { + var userCustomizations []blueprint.UserCustomization + for _, user := range *request.Customizations.Users { + var groups []string + if user.Groups != nil { + groups = *user.Groups + } else { + groups = nil + } + userCustomizations = append(userCustomizations, + blueprint.UserCustomization{ + Name: user.Name, + Key: user.Key, + Groups: groups, + }, + ) + } + blueprintCustoms = &blueprint.Customizations{ + User: userCustomizations, + } + } + + manifest, err := imageType.Manifest(blueprintCustoms, imageOptions, repositories, pkgSpecSets, manifestSeed) + if err != nil { + return HTTPErrorWithInternal(ErrorFailedToMakeManifest, err) + } + + imageRequest.manifest = manifest + imageRequest.arch = arch.Name() + imageRequest.exports = imageType.Exports() + + /* oneOf is not supported by the openapi generator so marshal and unmarshal the uploadrequest based on the type */ + switch ir.ImageType { + case ImageTypes_aws: + var awsUploadOptions AWSEC2UploadOptions + jsonUploadOptions, err := json.Marshal(ir.UploadOptions) + if err != nil { + return HTTPError(ErrorJSONMarshallingError) + } + err = json.Unmarshal(jsonUploadOptions, &awsUploadOptions) + if err != nil { + return HTTPError(ErrorJSONUnMarshallingError) + } + + key := fmt.Sprintf("composer-api-%s", uuid.New().String()) + t := target.NewAWSTarget(&target.AWSTargetOptions{ + Filename: imageType.Filename(), + Region: awsUploadOptions.Region, + Bucket: h.server.awsBucket, + Key: key, + ShareWithAccounts: awsUploadOptions.ShareWithAccounts, + }) + if awsUploadOptions.SnapshotName != nil { + t.ImageName = *awsUploadOptions.SnapshotName + } else { + t.ImageName = key + } + + imageRequest.target = t + case ImageTypes_edge_installer: + fallthrough + case ImageTypes_edge_commit: + var awsS3UploadOptions AWSS3UploadOptions + jsonUploadOptions, err := json.Marshal(ir.UploadOptions) + if err != nil { + return HTTPError(ErrorJSONMarshallingError) + } + err = json.Unmarshal(jsonUploadOptions, &awsS3UploadOptions) + if err != nil { + return HTTPError(ErrorJSONUnMarshallingError) + } + + key := fmt.Sprintf("composer-api-%s", uuid.New().String()) + t := target.NewAWSS3Target(&target.AWSS3TargetOptions{ + Filename: imageType.Filename(), + Region: awsS3UploadOptions.Region, + Bucket: h.server.awsBucket, + Key: key, + }) + t.ImageName = key + + imageRequest.target = t + case ImageTypes_gcp: + var gcpUploadOptions GCPUploadOptions + jsonUploadOptions, err := json.Marshal(ir.UploadOptions) + if err != nil { + return HTTPError(ErrorJSONMarshallingError) + } + err = json.Unmarshal(jsonUploadOptions, &gcpUploadOptions) + if err != nil { + return HTTPError(ErrorJSONUnMarshallingError) + } + + var share []string + if gcpUploadOptions.ShareWithAccounts != nil { + share = *gcpUploadOptions.ShareWithAccounts + } + + object := fmt.Sprintf("composer-api-%s", uuid.New().String()) + t := target.NewGCPTarget(&target.GCPTargetOptions{ + Filename: imageType.Filename(), + Region: gcpUploadOptions.Region, + Os: "", // not exposed in cloudapi for now + Bucket: gcpUploadOptions.Bucket, + Object: object, + ShareWithAccounts: share, + }) + // Import will fail if an image with this name already exists + if gcpUploadOptions.ImageName != nil { + t.ImageName = *gcpUploadOptions.ImageName + } else { + t.ImageName = object + } + + imageRequest.target = t + case ImageTypes_azure: + var azureUploadOptions AzureUploadOptions + jsonUploadOptions, err := json.Marshal(ir.UploadOptions) + if err != nil { + return HTTPError(ErrorJSONMarshallingError) + } + err = json.Unmarshal(jsonUploadOptions, &azureUploadOptions) + if err != nil { + return HTTPError(ErrorJSONUnMarshallingError) + } + t := target.NewAzureImageTarget(&target.AzureImageTargetOptions{ + Filename: imageType.Filename(), + TenantID: azureUploadOptions.TenantId, + Location: azureUploadOptions.Location, + SubscriptionID: azureUploadOptions.SubscriptionId, + ResourceGroup: azureUploadOptions.ResourceGroup, + }) + + if azureUploadOptions.ImageName != nil { + t.ImageName = *azureUploadOptions.ImageName + } else { + // if ImageName wasn't given, generate a random one + t.ImageName = fmt.Sprintf("composer-api-%s", uuid.New().String()) + } + + imageRequest.target = t + default: + return HTTPError(ErrorUnsupportedImageType) + } + + id, err := h.server.workers.EnqueueOSBuild(imageRequest.arch, &worker.OSBuildJob{ + Manifest: imageRequest.manifest, + Targets: []*target.Target{imageRequest.target}, + Exports: imageRequest.exports, }) if err != nil { return HTTPErrorWithInternal(ErrorEnqueueingJob, err) diff --git a/internal/cloudapi/v2/v2_test.go b/internal/cloudapi/v2/v2_test.go index 653f6a567..11644984d 100644 --- a/internal/cloudapi/v2/v2_test.go +++ b/internal/cloudapi/v2/v2_test.go @@ -134,7 +134,7 @@ func TestCompose(t *testing.T) { test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(` { "distribution": "unsupported_distro", - "image_requests":[{ + "image_request":{ "architecture": "%s", "image_type": "aws.ec2", "repositories": [{ @@ -145,7 +145,7 @@ func TestCompose(t *testing.T) { "region": "eu-central-1", "share_with_accounts": ["123456789012"] } - }] + } }`, test_distro.TestArch3Name), http.StatusBadRequest, ` { "href": "/api/image-builder-composer/v2/errors/4", @@ -159,7 +159,7 @@ func TestCompose(t *testing.T) { test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(` { "distribution": "%s", - "image_requests":[{ + "image_request":{ "architecture": "unsupported_arch", "image_type": "aws", "repositories": [{ @@ -169,7 +169,7 @@ func TestCompose(t *testing.T) { "upload_options": { "region": "eu-central-1" } - }] + } }`, test_distro.TestDistroName), http.StatusBadRequest, ` { "href": "/api/image-builder-composer/v2/errors/5", @@ -183,7 +183,7 @@ func TestCompose(t *testing.T) { test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(` { "distribution": "%s", - "image_requests":[{ + "image_request":{ "architecture": "%s", "image_type": "unsupported_image_type", "repositories": [{ @@ -193,7 +193,7 @@ func TestCompose(t *testing.T) { "upload_options": { "region": "eu-central-1" } - }] + } }`, test_distro.TestDistroName, test_distro.TestArch3Name), http.StatusBadRequest, ` { "href": "/api/image-builder-composer/v2/errors/6", @@ -230,7 +230,7 @@ func TestCompose(t *testing.T) { test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(` { "distribution": "%s", - "image_requests":[{ + "image_request":{ "architecture": "%s", "image_type": "aws", "repositories": [{ @@ -240,7 +240,7 @@ func TestCompose(t *testing.T) { "upload_options": { "region": "eu-central-1" } - }] + } }`, test_distro.TestDistroName, test_distro.TestArch3Name), http.StatusCreated, ` { "href": "/api/image-builder-composer/v2/compose", @@ -258,7 +258,7 @@ func TestComposeStatusSuccess(t *testing.T) { test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(` { "distribution": "%s", - "image_requests":[{ + "image_request":{ "architecture": "%s", "image_type": "aws", "repositories": [{ @@ -268,7 +268,7 @@ func TestComposeStatusSuccess(t *testing.T) { "upload_options": { "region": "eu-central-1" } - }] + } }`, test_distro.TestDistroName, test_distro.TestArch3Name), http.StatusCreated, ` { "href": "/api/image-builder-composer/v2/compose", @@ -324,7 +324,7 @@ func TestComposeStatusFailure(t *testing.T) { test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(` { "distribution": "%s", - "image_requests":[{ + "image_request":{ "architecture": "%s", "image_type": "aws", "repositories": [{ @@ -334,7 +334,7 @@ func TestComposeStatusFailure(t *testing.T) { "upload_options": { "region": "eu-central-1" } - }] + } }`, test_distro.TestDistroName, test_distro.TestArch3Name), http.StatusCreated, ` { "href": "/api/image-builder-composer/v2/compose", @@ -389,7 +389,7 @@ func TestComposeCustomizations(t *testing.T) { "key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINrGKErMYi+MMUwuHaRAJmRLoIzRf2qD2dD5z0BTx/6x" }] }, - "image_requests":[{ + "image_request":{ "architecture": "%s", "image_type": "aws", "repositories": [{ @@ -399,7 +399,7 @@ func TestComposeCustomizations(t *testing.T) { "upload_options": { "region": "eu-central-1" } - }] + } }`, test_distro.TestDistroName, test_distro.TestArch3Name), http.StatusCreated, ` { "href": "/api/image-builder-composer/v2/compose", @@ -417,7 +417,7 @@ func TestImageTypes(t *testing.T) { test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(` { "distribution": "%s", - "image_requests":[{ + "image_request":{ "architecture": "%s", "image_type": "%s", "repositories": [{ @@ -429,7 +429,7 @@ func TestImageTypes(t *testing.T) { "snapshot_name": "name", "share_with_accounts": ["123456789012","234567890123"] } - }] + } }`, test_distro.TestDistroName, test_distro.TestArch3Name, string(v2.ImageTypes_aws)), http.StatusCreated, ` { "href": "/api/image-builder-composer/v2/compose", @@ -438,7 +438,7 @@ func TestImageTypes(t *testing.T) { test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(` { "distribution": "%s", - "image_requests":[{ + "image_request":{ "architecture": "%s", "image_type": "%s", "repositories": [{ @@ -448,7 +448,7 @@ func TestImageTypes(t *testing.T) { "upload_options": { "region": "eu-central-1" } - }] + } }`, test_distro.TestDistroName, test_distro.TestArch3Name, string(v2.ImageTypes_aws)), http.StatusCreated, ` { "href": "/api/image-builder-composer/v2/compose", @@ -457,7 +457,7 @@ func TestImageTypes(t *testing.T) { test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(` { "distribution": "%s", - "image_requests":[{ + "image_request":{ "architecture": "%s", "image_type": "%s", "repositories": [{ @@ -467,7 +467,7 @@ func TestImageTypes(t *testing.T) { "upload_options": { "region": "eu-central-1" } - }] + } }`, test_distro.TestDistroName, test_distro.TestArch3Name, string(v2.ImageTypes_edge_commit)), http.StatusCreated, ` { "href": "/api/image-builder-composer/v2/compose", @@ -476,7 +476,7 @@ func TestImageTypes(t *testing.T) { test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(` { "distribution": "%s", - "image_requests":[{ + "image_request":{ "architecture": "%s", "image_type": "%s", "repositories": [{ @@ -486,7 +486,7 @@ func TestImageTypes(t *testing.T) { "upload_options": { "region": "eu-central-1" } - }] + } }`, test_distro.TestDistroName, test_distro.TestArch3Name, string(v2.ImageTypes_edge_installer)), http.StatusCreated, ` { "href": "/api/image-builder-composer/v2/compose", @@ -495,7 +495,7 @@ func TestImageTypes(t *testing.T) { test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(` { "distribution": "%s", - "image_requests":[{ + "image_request":{ "architecture": "%s", "image_type": "%s", "repositories": [{ @@ -508,7 +508,7 @@ func TestImageTypes(t *testing.T) { "resource_group": "ToucanResourceGroup", "location": "westeurope" } - }] + } }`, test_distro.TestDistroName, test_distro.TestArch3Name, string(v2.ImageTypes_azure)), http.StatusCreated, ` { "href": "/api/image-builder-composer/v2/compose", @@ -517,7 +517,7 @@ func TestImageTypes(t *testing.T) { test.TestRoute(t, srv.Handler("/api/image-builder-composer/v2"), false, "POST", "/api/image-builder-composer/v2/compose", fmt.Sprintf(` { "distribution": "%s", - "image_requests":[{ + "image_request":{ "architecture": "%s", "image_type": "%s", "repositories": [{ @@ -529,7 +529,7 @@ func TestImageTypes(t *testing.T) { "bucket": "some-eu-bucket", "share_with_accounts": ["user:alice@example.com"] } - }] + } }`, test_distro.TestDistroName, test_distro.TestArch3Name, string(v2.ImageTypes_gcp)), http.StatusCreated, ` { "href": "/api/image-builder-composer/v2/compose",