mirror of
https://github.com/portainer/portainer.git
synced 2026-08-07 10:54:48 +00:00
fix(api-docs) fix api spec and handler mismatches (#3104)
This commit is contained in:
+100
-9
@@ -4839,7 +4839,7 @@ paths:
|
||||
"/kubernetes/{id}/namespaces":
|
||||
delete:
|
||||
description: |-
|
||||
Delete a kubernetes namespace within the given environment.
|
||||
Delete one or more kubernetes namespaces within the given environment.
|
||||
**Access policy**: Authenticated user.
|
||||
operationId: DeleteKubernetesNamespace
|
||||
parameters:
|
||||
@@ -4849,6 +4849,15 @@ paths:
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
description: List of namespace names to delete
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: Success
|
||||
@@ -4866,7 +4875,7 @@ paths:
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
jwt: []
|
||||
summary: Delete a kubernetes namespace
|
||||
summary: Delete kubernetes namespaces
|
||||
tags:
|
||||
- kubernetes
|
||||
get:
|
||||
@@ -4889,14 +4898,12 @@ paths:
|
||||
the Namespace information. Default is false
|
||||
in: query
|
||||
name: withResourceQuota
|
||||
required: true
|
||||
schema:
|
||||
type: boolean
|
||||
- description: When set to true, include the unhealthy events information as part
|
||||
of the Namespace information. Default is false
|
||||
in: query
|
||||
name: withUnhealthyEvents
|
||||
required: true
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
@@ -4956,7 +4963,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/portainer.K8sNamespaceInfo"
|
||||
$ref: "#/components/schemas/kubernetes.KubernetesCreateNamespaceResponse"
|
||||
"400":
|
||||
description: Invalid request payload, such as missing required fields or fields
|
||||
not meeting validation criteria.
|
||||
@@ -5054,7 +5061,6 @@ paths:
|
||||
the Namespace information. Default is false
|
||||
in: query
|
||||
name: withResourceQuota
|
||||
required: true
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
@@ -8589,7 +8595,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/portainer.Stack"
|
||||
$ref: "#/components/schemas/stacks.createKubernetesStackResponse"
|
||||
"400":
|
||||
description: Invalid request
|
||||
"409":
|
||||
@@ -8630,7 +8636,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/portainer.Stack"
|
||||
$ref: "#/components/schemas/stacks.createKubernetesStackResponse"
|
||||
"400":
|
||||
description: Invalid request
|
||||
"500":
|
||||
@@ -8669,7 +8675,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/portainer.Stack"
|
||||
$ref: "#/components/schemas/stacks.createKubernetesStackResponse"
|
||||
"400":
|
||||
description: Invalid request
|
||||
"500":
|
||||
@@ -13240,6 +13246,64 @@ components:
|
||||
storageClass:
|
||||
$ref: "#/components/schemas/kubernetes.K8sStorageClass"
|
||||
type: object
|
||||
kubernetes.KubernetesCreateNamespaceResponse:
|
||||
properties:
|
||||
apiVersion:
|
||||
description: >-
|
||||
APIVersion defines the versioned schema of this representation of an
|
||||
object.
|
||||
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
|
||||
may reject unrecognized values.
|
||||
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
|
||||
+optional
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is a string value representing the REST resource this object
|
||||
represents.
|
||||
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
|
||||
Cannot be updated.
|
||||
|
||||
In CamelCase.
|
||||
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
|
||||
+optional
|
||||
type: string
|
||||
metadata:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/v1.ObjectMeta"
|
||||
description: >-
|
||||
Standard object's metadata.
|
||||
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||
|
||||
+optional
|
||||
spec:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/v1.NamespaceSpec"
|
||||
description: >-
|
||||
Spec defines the behavior of the Namespace.
|
||||
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
|
||||
+optional
|
||||
status:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/v1.NamespaceStatus"
|
||||
description: >-
|
||||
Status describes the current status of a Namespace.
|
||||
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
|
||||
+optional
|
||||
type: object
|
||||
kubernetes.KubernetesNodeResponse:
|
||||
properties:
|
||||
apiVersion:
|
||||
@@ -16889,6 +16953,11 @@ components:
|
||||
required:
|
||||
- Name
|
||||
type: object
|
||||
stacks.createKubernetesStackResponse:
|
||||
properties:
|
||||
Output:
|
||||
type: string
|
||||
type: object
|
||||
stacks.kubernetesGitDeploymentPayload:
|
||||
properties:
|
||||
AdditionalFiles:
|
||||
@@ -18699,6 +18768,12 @@ components:
|
||||
+required
|
||||
type: string
|
||||
type: object
|
||||
v1.FinalizerName:
|
||||
enum:
|
||||
- kubernetes
|
||||
type: string
|
||||
x-enum-varnames:
|
||||
- FinalizerKubernetes
|
||||
v1.GRPCAction:
|
||||
properties:
|
||||
port:
|
||||
@@ -19042,6 +19117,22 @@ components:
|
||||
x-enum-varnames:
|
||||
- NamespaceActive
|
||||
- NamespaceTerminating
|
||||
v1.NamespaceSpec:
|
||||
properties:
|
||||
finalizers:
|
||||
description: >-
|
||||
Finalizers is an opaque list of values that must be empty to
|
||||
permanently remove object from storage.
|
||||
|
||||
More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/
|
||||
|
||||
+optional
|
||||
|
||||
+listType=atomic
|
||||
items:
|
||||
$ref: "#/components/schemas/v1.FinalizerName"
|
||||
type: array
|
||||
type: object
|
||||
v1.NamespaceStatus:
|
||||
properties:
|
||||
conditions:
|
||||
|
||||
+80
-9
@@ -2342,6 +2342,47 @@ definitions:
|
||||
storageClass:
|
||||
$ref: '#/definitions/kubernetes.K8sStorageClass'
|
||||
type: object
|
||||
kubernetes.KubernetesCreateNamespaceResponse:
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
+optional
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
+optional
|
||||
type: string
|
||||
metadata:
|
||||
allOf:
|
||||
- $ref: '#/definitions/v1.ObjectMeta'
|
||||
description: |-
|
||||
Standard object's metadata.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||
+optional
|
||||
spec:
|
||||
allOf:
|
||||
- $ref: '#/definitions/v1.NamespaceSpec'
|
||||
description: |-
|
||||
Spec defines the behavior of the Namespace.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
+optional
|
||||
status:
|
||||
allOf:
|
||||
- $ref: '#/definitions/v1.NamespaceStatus'
|
||||
description: |-
|
||||
Status describes the current status of a Namespace.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
+optional
|
||||
type: object
|
||||
kubernetes.KubernetesNodeResponse:
|
||||
properties:
|
||||
apiVersion:
|
||||
@@ -5914,6 +5955,11 @@ definitions:
|
||||
required:
|
||||
- Name
|
||||
type: object
|
||||
stacks.createKubernetesStackResponse:
|
||||
properties:
|
||||
Output:
|
||||
type: string
|
||||
type: object
|
||||
stacks.kubernetesGitDeploymentPayload:
|
||||
properties:
|
||||
AdditionalFiles:
|
||||
@@ -7449,6 +7495,12 @@ definitions:
|
||||
+required
|
||||
type: string
|
||||
type: object
|
||||
v1.FinalizerName:
|
||||
enum:
|
||||
- kubernetes
|
||||
type: string
|
||||
x-enum-varnames:
|
||||
- FinalizerKubernetes
|
||||
v1.GRPCAction:
|
||||
properties:
|
||||
port:
|
||||
@@ -7720,6 +7772,18 @@ definitions:
|
||||
x-enum-varnames:
|
||||
- NamespaceActive
|
||||
- NamespaceTerminating
|
||||
v1.NamespaceSpec:
|
||||
properties:
|
||||
finalizers:
|
||||
description: |-
|
||||
Finalizers is an opaque list of values that must be empty to permanently remove object from storage.
|
||||
More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/
|
||||
+optional
|
||||
+listType=atomic
|
||||
items:
|
||||
$ref: '#/definitions/v1.FinalizerName'
|
||||
type: array
|
||||
type: object
|
||||
v1.NamespaceStatus:
|
||||
properties:
|
||||
conditions:
|
||||
@@ -14326,8 +14390,10 @@ paths:
|
||||
- kubernetes
|
||||
/kubernetes/{id}/namespaces:
|
||||
delete:
|
||||
consumes:
|
||||
- application/json
|
||||
description: |-
|
||||
Delete a kubernetes namespace within the given environment.
|
||||
Delete one or more kubernetes namespaces within the given environment.
|
||||
**Access policy**: Authenticated user.
|
||||
operationId: DeleteKubernetesNamespace
|
||||
parameters:
|
||||
@@ -14336,6 +14402,14 @@ paths:
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
- description: List of namespace names to delete
|
||||
in: body
|
||||
name: body
|
||||
required: true
|
||||
schema:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
responses:
|
||||
"200":
|
||||
description: Success
|
||||
@@ -14351,7 +14425,7 @@ paths:
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
jwt: []
|
||||
summary: Delete a kubernetes namespace
|
||||
summary: Delete kubernetes namespaces
|
||||
tags:
|
||||
- kubernetes
|
||||
get:
|
||||
@@ -14369,13 +14443,11 @@ paths:
|
||||
of the Namespace information. Default is false
|
||||
in: query
|
||||
name: withResourceQuota
|
||||
required: true
|
||||
type: boolean
|
||||
- description: When set to true, include the unhealthy events information as
|
||||
part of the Namespace information. Default is false
|
||||
in: query
|
||||
name: withUnhealthyEvents
|
||||
required: true
|
||||
type: boolean
|
||||
produces:
|
||||
- application/json
|
||||
@@ -14433,7 +14505,7 @@ paths:
|
||||
"200":
|
||||
description: Success
|
||||
schema:
|
||||
$ref: '#/definitions/portainer.K8sNamespaceInfo'
|
||||
$ref: '#/definitions/kubernetes.KubernetesCreateNamespaceResponse'
|
||||
"400":
|
||||
description: Invalid request payload, such as missing required fields or
|
||||
fields not meeting validation criteria.
|
||||
@@ -14529,7 +14601,6 @@ paths:
|
||||
of the Namespace information. Default is false
|
||||
in: query
|
||||
name: withResourceQuota
|
||||
required: true
|
||||
type: boolean
|
||||
produces:
|
||||
- application/json
|
||||
@@ -17952,7 +18023,7 @@ paths:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/portainer.Stack'
|
||||
$ref: '#/definitions/stacks.createKubernetesStackResponse'
|
||||
"400":
|
||||
description: Invalid request
|
||||
"409":
|
||||
@@ -17990,7 +18061,7 @@ paths:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/portainer.Stack'
|
||||
$ref: '#/definitions/stacks.createKubernetesStackResponse'
|
||||
"400":
|
||||
description: Invalid request
|
||||
"500":
|
||||
@@ -18026,7 +18097,7 @@ paths:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/portainer.Stack'
|
||||
$ref: '#/definitions/stacks.createKubernetesStackResponse'
|
||||
"400":
|
||||
description: Invalid request
|
||||
"500":
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
"github.com/rs/zerolog/log"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
)
|
||||
|
||||
@@ -21,8 +22,8 @@ import (
|
||||
// @security ApiKeyAuth || jwt
|
||||
// @produce json
|
||||
// @param id path int true "Environment identifier"
|
||||
// @param withResourceQuota query boolean true "When set to true, include the resource quota information as part of the Namespace information. Default is false"
|
||||
// @param withUnhealthyEvents query boolean true "When set to true, include the unhealthy events information as part of the Namespace information. Default is false"
|
||||
// @param withResourceQuota query boolean false "When set to true, include the resource quota information as part of the Namespace information. Default is false"
|
||||
// @param withUnhealthyEvents query boolean false "When set to true, include the unhealthy events information as part of the Namespace information. Default is false"
|
||||
// @success 200 {array} portainer.K8sNamespaceInfo "Success"
|
||||
// @failure 400 "Invalid request payload, such as missing required fields or fields not meeting validation criteria."
|
||||
// @failure 401 "Unauthorized access - the user is not authenticated or does not have the necessary permissions. Ensure that you have provided a valid API key or JWT token, and that you have the required permissions."
|
||||
@@ -110,7 +111,7 @@ func (handler *Handler) getKubernetesNamespacesCount(w http.ResponseWriter, r *h
|
||||
// @produce json
|
||||
// @param id path int true "Environment identifier"
|
||||
// @param namespace path string true "The namespace name to get details for"
|
||||
// @param withResourceQuota query boolean true "When set to true, include the resource quota information as part of the Namespace information. Default is false"
|
||||
// @param withResourceQuota query boolean false "When set to true, include the resource quota information as part of the Namespace information. Default is false"
|
||||
// @success 200 {object} portainer.K8sNamespaceInfo "Success"
|
||||
// @failure 400 "Invalid request payload, such as missing required fields or fields not meeting validation criteria."
|
||||
// @failure 401 "Unauthorized access - the user is not authenticated or does not have the necessary permissions. Ensure that you have provided a valid API key or JWT token, and that you have the required permissions."
|
||||
@@ -170,7 +171,7 @@ func (handler *Handler) getKubernetesNamespace(w http.ResponseWriter, r *http.Re
|
||||
// @produce json
|
||||
// @param id path int true "Environment identifier"
|
||||
// @param body body models.K8sNamespaceDetails true "Namespace configuration details"
|
||||
// @success 200 {object} portainer.K8sNamespaceInfo "Success"
|
||||
// @success 200 {object} KubernetesCreateNamespaceResponse "Success"
|
||||
// @failure 400 "Invalid request payload, such as missing required fields or fields not meeting validation criteria."
|
||||
// @failure 401 "Unauthorized access - the user is not authenticated or does not have the necessary permissions. Ensure that you have provided a valid API key or JWT token, and that you have the required permissions."
|
||||
// @failure 403 "Permission denied - the user is authenticated but does not have the necessary permissions to access the requested resource or perform the specified operation. Check your user roles and permissions."
|
||||
@@ -207,12 +208,14 @@ func (handler *Handler) createKubernetesNamespace(w http.ResponseWriter, r *http
|
||||
}
|
||||
|
||||
// @id DeleteKubernetesNamespace
|
||||
// @summary Delete a kubernetes namespace
|
||||
// @description Delete a kubernetes namespace within the given environment.
|
||||
// @summary Delete kubernetes namespaces
|
||||
// @description Delete one or more kubernetes namespaces within the given environment.
|
||||
// @description **Access policy**: Authenticated user.
|
||||
// @tags kubernetes
|
||||
// @security ApiKeyAuth || jwt
|
||||
// @accept json
|
||||
// @param id path int true "Environment identifier"
|
||||
// @param body body deleteKubernetesNamespacePayload true "List of namespace names to delete"
|
||||
// @success 200 {string} string "Success"
|
||||
// @failure 400 "Invalid request payload, such as missing required fields or fields not meeting validation criteria."
|
||||
// @failure 403 "Unauthorized access or operation not allowed."
|
||||
@@ -221,8 +224,8 @@ func (handler *Handler) createKubernetesNamespace(w http.ResponseWriter, r *http
|
||||
func (handler *Handler) deleteKubernetesNamespace(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
namespaceNames, err := request.GetPayload[deleteKubernetesNamespacePayload](r)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("context", "DeleteKubernetesNamespace").Msg("Invalid namespace identifier route variable")
|
||||
return httperror.BadRequest("an error occurred during the DeleteKubernetesNamespace operation, invalid namespace identifier route variable. Error: ", err)
|
||||
log.Error().Err(err).Str("context", "DeleteKubernetesNamespace").Msg("Invalid request payload")
|
||||
return httperror.BadRequest("an error occurred during the DeleteKubernetesNamespace operation, invalid request payload. Error: ", err)
|
||||
}
|
||||
|
||||
cli, httpErr := handler.getProxyKubeClient(r)
|
||||
@@ -256,6 +259,9 @@ func (payload deleteKubernetesNamespacePayload) Validate(r *http.Request) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// KubernetesCreateNamespaceResponse is the documented response model for namespace create endpoints.
|
||||
type KubernetesCreateNamespaceResponse corev1.Namespace
|
||||
|
||||
// @id UpdateKubernetesNamespace
|
||||
// @summary Update a namespace
|
||||
// @description Update a namespace within the given environment.
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/api/jwt"
|
||||
"github.com/portainer/portainer/api/kubernetes"
|
||||
kubeclient "github.com/portainer/portainer/api/kubernetes/cli"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func newNamespaceTestHandler(t *testing.T) (*Handler, *portainer.User, string) {
|
||||
t.Helper()
|
||||
|
||||
srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{}`))
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
_, store := datastore.MustNewTestStore(t, true, true)
|
||||
|
||||
err := store.Endpoint().Create(&portainer.Endpoint{
|
||||
ID: 1,
|
||||
Type: portainer.KubernetesLocalEnvironment,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
u := &portainer.User{Username: "admin", Role: portainer.AdministratorRole}
|
||||
err = store.User().Create(u)
|
||||
require.NoError(t, err)
|
||||
|
||||
jwtService, err := jwt.NewService("1h", store)
|
||||
require.NoError(t, err)
|
||||
|
||||
tk, _, err := jwtService.GenerateToken(&portainer.TokenData{ID: u.ID, Username: u.Username, Role: u.Role})
|
||||
require.NoError(t, err)
|
||||
|
||||
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService("", "", "")
|
||||
|
||||
srvURL, err := url.Parse(srv.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
cli := testhelpers.NewKubernetesClient()
|
||||
factory, err := kubeclient.NewClientFactory(nil, nil, store, "", ":"+srvURL.Port(), "")
|
||||
require.NoError(t, err)
|
||||
|
||||
authorizationService := authorization.NewService(store)
|
||||
handler := NewHandler(testhelpers.NewTestRequestBouncer(), authorizationService, store, jwtService, kubeClusterAccessService, factory, cli)
|
||||
|
||||
return handler, u, tk
|
||||
}
|
||||
|
||||
func newNamespaceRequest(t *testing.T, method, path string, body any, u *portainer.User, tk string) *http.Request {
|
||||
t.Helper()
|
||||
|
||||
var bodyReader *bytes.Reader
|
||||
if body != nil {
|
||||
b, err := json.Marshal(body)
|
||||
require.NoError(t, err)
|
||||
bodyReader = bytes.NewReader(b)
|
||||
} else {
|
||||
bodyReader = bytes.NewReader(nil)
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(method, path, bodyReader)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
ctx := security.StoreTokenData(req, &portainer.TokenData{ID: u.ID, Username: u.Username, Role: u.Role})
|
||||
req = req.WithContext(ctx)
|
||||
ctx = security.StoreRestrictedRequestContext(req, &security.RestrictedRequestContext{IsAdmin: true, UserID: u.ID})
|
||||
req = req.WithContext(ctx)
|
||||
testhelpers.AddTestSecurityCookie(req, tk)
|
||||
return req
|
||||
}
|
||||
|
||||
func TestDeleteKubernetesNamespace_InvalidPayload(t *testing.T) {
|
||||
t.Parallel()
|
||||
handler, u, tk := newNamespaceTestHandler(t)
|
||||
|
||||
req := httptest.NewRequest(http.MethodDelete, "/kubernetes/1/namespaces", strings.NewReader("not-valid-json"))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
ctx := security.StoreTokenData(req, &portainer.TokenData{ID: u.ID, Username: u.Username, Role: u.Role})
|
||||
req = req.WithContext(ctx)
|
||||
ctx = security.StoreRestrictedRequestContext(req, &security.RestrictedRequestContext{IsAdmin: true, UserID: u.ID})
|
||||
req = req.WithContext(ctx)
|
||||
testhelpers.AddTestSecurityCookie(req, tk)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, rr.Code)
|
||||
}
|
||||
|
||||
func TestDeleteKubernetesNamespace_ReachesKubernetesLayer(t *testing.T) {
|
||||
t.Parallel()
|
||||
handler, u, tk := newNamespaceTestHandler(t)
|
||||
|
||||
req := newNamespaceRequest(t, http.MethodDelete, "/kubernetes/1/namespaces", []string{"default"}, u, tk)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.NotEqual(t, http.StatusBadRequest, rr.Code, "should not be rejected at the handler layer")
|
||||
assert.NotEqual(t, http.StatusNotFound, rr.Code, "route must be registered")
|
||||
}
|
||||
@@ -149,7 +149,7 @@ type createKubernetesStackResponse struct {
|
||||
// @produce json
|
||||
// @param body body kubernetesStringDeploymentPayload true "stack config"
|
||||
// @param endpointId query int true "Identifier of the environment that will be used to deploy the stack"
|
||||
// @success 200 {object} portainer.Stack
|
||||
// @success 200 {object} createKubernetesStackResponse
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 500 "Server error"
|
||||
// @router /stacks/create/kubernetes/string [post]
|
||||
@@ -207,7 +207,7 @@ func (handler *Handler) createKubernetesStackFromFileContent(w http.ResponseWrit
|
||||
// @produce json
|
||||
// @param body body kubernetesGitDeploymentPayload true "stack config"
|
||||
// @param endpointId query int true "Identifier of the environment that will be used to deploy the stack"
|
||||
// @success 200 {object} portainer.Stack
|
||||
// @success 200 {object} createKubernetesStackResponse
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 409 "Stack name or webhook ID already exists"
|
||||
// @failure 500 "Server error"
|
||||
@@ -289,7 +289,7 @@ func (handler *Handler) createKubernetesStackFromGitRepository(w http.ResponseWr
|
||||
// @produce json
|
||||
// @param body body kubernetesManifestURLDeploymentPayload true "stack config"
|
||||
// @param endpointId query int true "Identifier of the environment that will be used to deploy the stack"
|
||||
// @success 200 {object} portainer.Stack
|
||||
// @success 200 {object} createKubernetesStackResponse
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 500 "Server error"
|
||||
// @router /stacks/create/kubernetes/url [post]
|
||||
|
||||
@@ -834,6 +834,7 @@ import {
|
||||
zDeleteKubernetesIngressesBody,
|
||||
zDeleteKubernetesIngressesPath,
|
||||
zDeleteKubernetesIngressesResponse,
|
||||
zDeleteKubernetesNamespaceBody,
|
||||
zDeleteKubernetesNamespacePath,
|
||||
zDeleteKubernetesNamespaceResponse,
|
||||
zDeleteKubernetesPersistentVolumeClaimsBody,
|
||||
@@ -5534,9 +5535,9 @@ export const getKubernetesMetricsForPod = <ThrowOnError extends boolean = true>(
|
||||
});
|
||||
|
||||
/**
|
||||
* Delete a kubernetes namespace
|
||||
* Delete kubernetes namespaces
|
||||
*
|
||||
* Delete a kubernetes namespace within the given environment.
|
||||
* Delete one or more kubernetes namespaces within the given environment.
|
||||
* **Access policy**: Authenticated user.
|
||||
*/
|
||||
export const deleteKubernetesNamespace = <ThrowOnError extends boolean = true>(
|
||||
@@ -5554,7 +5555,7 @@ export const deleteKubernetesNamespace = <ThrowOnError extends boolean = true>(
|
||||
requestValidator: async (data) =>
|
||||
await z
|
||||
.object({
|
||||
body: z.never().optional(),
|
||||
body: zDeleteKubernetesNamespaceBody,
|
||||
path: zDeleteKubernetesNamespacePath,
|
||||
query: z.never().optional(),
|
||||
})
|
||||
@@ -5567,6 +5568,10 @@ export const deleteKubernetesNamespace = <ThrowOnError extends boolean = true>(
|
||||
],
|
||||
url: '/kubernetes/{id}/namespaces',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -5592,7 +5597,7 @@ export const getKubernetesNamespaces = <ThrowOnError extends boolean = true>(
|
||||
.object({
|
||||
body: z.never().optional(),
|
||||
path: zGetKubernetesNamespacesPath,
|
||||
query: zGetKubernetesNamespacesQuery,
|
||||
query: zGetKubernetesNamespacesQuery.optional(),
|
||||
})
|
||||
.parseAsync(data),
|
||||
responseType: 'json',
|
||||
@@ -5713,7 +5718,7 @@ export const getKubernetesNamespace = <ThrowOnError extends boolean = true>(
|
||||
.object({
|
||||
body: z.never().optional(),
|
||||
path: zGetKubernetesNamespacePath,
|
||||
query: zGetKubernetesNamespaceQuery,
|
||||
query: zGetKubernetesNamespaceQuery.optional(),
|
||||
})
|
||||
.parseAsync(data),
|
||||
responseType: 'json',
|
||||
|
||||
@@ -1479,6 +1479,44 @@ export type KubernetesK8sVolumeInfo = {
|
||||
storageClass?: KubernetesK8sStorageClass;
|
||||
};
|
||||
|
||||
export type KubernetesKubernetesCreateNamespaceResponse = {
|
||||
/**
|
||||
* APIVersion defines the versioned schema of this representation of an object.
|
||||
* Servers should convert recognized schemas to the latest internal value, and
|
||||
* may reject unrecognized values.
|
||||
* More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
* +optional
|
||||
*/
|
||||
apiVersion?: string;
|
||||
/**
|
||||
* Kind is a string value representing the REST resource this object represents.
|
||||
* Servers may infer this from the endpoint the client submits requests to.
|
||||
* Cannot be updated.
|
||||
* In CamelCase.
|
||||
* More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
* +optional
|
||||
*/
|
||||
kind?: string;
|
||||
/**
|
||||
* Standard object's metadata.
|
||||
* More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||
* +optional
|
||||
*/
|
||||
metadata?: V1ObjectMeta;
|
||||
/**
|
||||
* Spec defines the behavior of the Namespace.
|
||||
* More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
* +optional
|
||||
*/
|
||||
spec?: V1NamespaceSpec;
|
||||
/**
|
||||
* Status describes the current status of a Namespace.
|
||||
* More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
* +optional
|
||||
*/
|
||||
status?: V1NamespaceStatus;
|
||||
};
|
||||
|
||||
export type KubernetesKubernetesNodeResponse = {
|
||||
/**
|
||||
* APIVersion defines the versioned schema of this representation of an object.
|
||||
@@ -4525,6 +4563,10 @@ export type StacksComposeStackFromGitRepositoryPayload = {
|
||||
TLSSkipVerify?: boolean;
|
||||
};
|
||||
|
||||
export type StacksCreateKubernetesStackResponse = {
|
||||
Output?: string;
|
||||
};
|
||||
|
||||
export type StacksKubernetesGitDeploymentPayload = {
|
||||
AdditionalFiles?: Array<string>;
|
||||
AutoUpdate?: PortainerAutoUpdateSettings;
|
||||
@@ -5845,6 +5887,16 @@ export type V1FileKeySelector = {
|
||||
volumeName?: string;
|
||||
};
|
||||
|
||||
export const V1FinalizerName = {
|
||||
/**
|
||||
* FinalizerKubernetes
|
||||
*/
|
||||
FINALIZER_KUBERNETES: 'kubernetes',
|
||||
} as const;
|
||||
|
||||
export type V1FinalizerName =
|
||||
(typeof V1FinalizerName)[keyof typeof V1FinalizerName];
|
||||
|
||||
export type V1GrpcAction = {
|
||||
/**
|
||||
* Port number of the gRPC service. Number must be in the range 1 to 65535.
|
||||
@@ -6135,6 +6187,16 @@ export const V1NamespacePhase = {
|
||||
export type V1NamespacePhase =
|
||||
(typeof V1NamespacePhase)[keyof typeof V1NamespacePhase];
|
||||
|
||||
export type V1NamespaceSpec = {
|
||||
/**
|
||||
* Finalizers is an opaque list of values that must be empty to permanently remove object from storage.
|
||||
* More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/
|
||||
* +optional
|
||||
* +listType=atomic
|
||||
*/
|
||||
finalizers?: Array<V1FinalizerName>;
|
||||
};
|
||||
|
||||
export type V1NamespaceStatus = {
|
||||
/**
|
||||
* Represents the latest available observations of a namespace's current state.
|
||||
@@ -13123,7 +13185,10 @@ export type GetKubernetesMetricsForPodResponse =
|
||||
GetKubernetesMetricsForPodResponses[keyof GetKubernetesMetricsForPodResponses];
|
||||
|
||||
export type DeleteKubernetesNamespaceData = {
|
||||
body?: never;
|
||||
/**
|
||||
* List of namespace names to delete
|
||||
*/
|
||||
body: Array<string>;
|
||||
path: {
|
||||
/**
|
||||
* Environment identifier
|
||||
@@ -13167,15 +13232,15 @@ export type GetKubernetesNamespacesData = {
|
||||
*/
|
||||
id: number;
|
||||
};
|
||||
query: {
|
||||
query?: {
|
||||
/**
|
||||
* When set to true, include the resource quota information as part of the Namespace information. Default is false
|
||||
*/
|
||||
withResourceQuota: boolean;
|
||||
withResourceQuota?: boolean;
|
||||
/**
|
||||
* When set to true, include the unhealthy events information as part of the Namespace information. Default is false
|
||||
*/
|
||||
withUnhealthyEvents: boolean;
|
||||
withUnhealthyEvents?: boolean;
|
||||
};
|
||||
url: '/kubernetes/{id}/namespaces';
|
||||
};
|
||||
@@ -13255,7 +13320,7 @@ export type CreateKubernetesNamespaceResponses = {
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
200: PortainerK8sNamespaceInfo;
|
||||
200: KubernetesKubernetesCreateNamespaceResponse;
|
||||
};
|
||||
|
||||
export type CreateKubernetesNamespaceResponse =
|
||||
@@ -13325,11 +13390,11 @@ export type GetKubernetesNamespaceData = {
|
||||
*/
|
||||
namespace: string;
|
||||
};
|
||||
query: {
|
||||
query?: {
|
||||
/**
|
||||
* When set to true, include the resource quota information as part of the Namespace information. Default is false
|
||||
*/
|
||||
withResourceQuota: boolean;
|
||||
withResourceQuota?: boolean;
|
||||
};
|
||||
url: '/kubernetes/{id}/namespaces/{namespace}';
|
||||
};
|
||||
@@ -17021,7 +17086,7 @@ export type StackCreateKubernetesGitResponses = {
|
||||
/**
|
||||
* OK
|
||||
*/
|
||||
200: PortainerStack;
|
||||
200: StacksCreateKubernetesStackResponse;
|
||||
};
|
||||
|
||||
export type StackCreateKubernetesGitResponse =
|
||||
@@ -17057,7 +17122,7 @@ export type StackCreateKubernetesFileResponses = {
|
||||
/**
|
||||
* OK
|
||||
*/
|
||||
200: PortainerStack;
|
||||
200: StacksCreateKubernetesStackResponse;
|
||||
};
|
||||
|
||||
export type StackCreateKubernetesFileResponse =
|
||||
@@ -17093,7 +17158,7 @@ export type StackCreateKubernetesUrlResponses = {
|
||||
/**
|
||||
* OK
|
||||
*/
|
||||
200: PortainerStack;
|
||||
200: StacksCreateKubernetesStackResponse;
|
||||
};
|
||||
|
||||
export type StackCreateKubernetesUrlResponse =
|
||||
|
||||
@@ -2120,6 +2120,10 @@ export const zStacksComposeStackFromGitRepositoryPayload = z.object({
|
||||
TLSSkipVerify: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export const zStacksCreateKubernetesStackResponse = z.object({
|
||||
Output: z.string().optional(),
|
||||
});
|
||||
|
||||
export const zStacksKubernetesGitDeploymentPayload = z.object({
|
||||
AdditionalFiles: z.array(z.string()).optional(),
|
||||
AutoUpdate: zPortainerAutoUpdateSettings.optional(),
|
||||
@@ -2511,6 +2515,8 @@ export const zV1FileKeySelector = z.object({
|
||||
volumeName: z.string().optional(),
|
||||
});
|
||||
|
||||
export const zV1FinalizerName = z.enum(['kubernetes']);
|
||||
|
||||
export const zV1GrpcAction = z.object({
|
||||
port: z.int().optional(),
|
||||
service: z.string().optional(),
|
||||
@@ -2559,6 +2565,10 @@ export const zV1NamespaceCondition = z.object({
|
||||
|
||||
export const zV1NamespacePhase = z.enum(['Active', 'Terminating']);
|
||||
|
||||
export const zV1NamespaceSpec = z.object({
|
||||
finalizers: z.array(zV1FinalizerName).optional(),
|
||||
});
|
||||
|
||||
export const zV1NamespaceStatus = z.object({
|
||||
conditions: z.array(zV1NamespaceCondition).optional(),
|
||||
phase: zV1NamespacePhase.optional(),
|
||||
@@ -2675,6 +2685,14 @@ export const zV1ObjectMeta = z.object({
|
||||
uid: z.string().optional(),
|
||||
});
|
||||
|
||||
export const zKubernetesKubernetesCreateNamespaceResponse = z.object({
|
||||
apiVersion: z.string().optional(),
|
||||
kind: z.string().optional(),
|
||||
metadata: zV1ObjectMeta.optional(),
|
||||
spec: zV1NamespaceSpec.optional(),
|
||||
status: zV1NamespaceStatus.optional(),
|
||||
});
|
||||
|
||||
export const zV1PersistentVolumeAccessMode = z.enum([
|
||||
'ReadWriteOnce',
|
||||
'ReadOnlyMany',
|
||||
@@ -4796,6 +4814,11 @@ export const zGetKubernetesMetricsForPodPath = z.object({
|
||||
*/
|
||||
export const zGetKubernetesMetricsForPodResponse = zV1Beta1PodMetrics;
|
||||
|
||||
/**
|
||||
* List of namespace names to delete
|
||||
*/
|
||||
export const zDeleteKubernetesNamespaceBody = z.array(z.string());
|
||||
|
||||
export const zDeleteKubernetesNamespacePath = z.object({
|
||||
id: z.int(),
|
||||
});
|
||||
@@ -4810,8 +4833,8 @@ export const zGetKubernetesNamespacesPath = z.object({
|
||||
});
|
||||
|
||||
export const zGetKubernetesNamespacesQuery = z.object({
|
||||
withResourceQuota: z.boolean(),
|
||||
withUnhealthyEvents: z.boolean(),
|
||||
withResourceQuota: z.boolean().optional(),
|
||||
withUnhealthyEvents: z.boolean().optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -4833,7 +4856,8 @@ export const zCreateKubernetesNamespacePath = z.object({
|
||||
/**
|
||||
* Success
|
||||
*/
|
||||
export const zCreateKubernetesNamespaceResponse = zPortainerK8sNamespaceInfo;
|
||||
export const zCreateKubernetesNamespaceResponse =
|
||||
zKubernetesKubernetesCreateNamespaceResponse;
|
||||
|
||||
/**
|
||||
* Namespace details
|
||||
@@ -4858,7 +4882,7 @@ export const zGetKubernetesNamespacePath = z.object({
|
||||
});
|
||||
|
||||
export const zGetKubernetesNamespaceQuery = z.object({
|
||||
withResourceQuota: z.boolean(),
|
||||
withResourceQuota: z.boolean().optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -5855,7 +5879,8 @@ export const zStackCreateKubernetesGitQuery = z.object({
|
||||
/**
|
||||
* OK
|
||||
*/
|
||||
export const zStackCreateKubernetesGitResponse = zPortainerStack;
|
||||
export const zStackCreateKubernetesGitResponse =
|
||||
zStacksCreateKubernetesStackResponse;
|
||||
|
||||
/**
|
||||
* stack config
|
||||
@@ -5870,7 +5895,8 @@ export const zStackCreateKubernetesFileQuery = z.object({
|
||||
/**
|
||||
* OK
|
||||
*/
|
||||
export const zStackCreateKubernetesFileResponse = zPortainerStack;
|
||||
export const zStackCreateKubernetesFileResponse =
|
||||
zStacksCreateKubernetesStackResponse;
|
||||
|
||||
/**
|
||||
* stack config
|
||||
@@ -5885,7 +5911,8 @@ export const zStackCreateKubernetesUrlQuery = z.object({
|
||||
/**
|
||||
* OK
|
||||
*/
|
||||
export const zStackCreateKubernetesUrlResponse = zPortainerStack;
|
||||
export const zStackCreateKubernetesUrlResponse =
|
||||
zStacksCreateKubernetesStackResponse;
|
||||
|
||||
export const zStackCreateDockerStandaloneFileBody = z.object({
|
||||
Name: z.string(),
|
||||
|
||||
Reference in New Issue
Block a user