mirror of https://github.com/portainer/portainer
fix(edge/stacks): validate deployment type [EE-4580] (#8875)
parent
334eee0c8c
commit
5f6ddc2fad
|
@ -14,11 +14,10 @@ type edgeStackFromFileUploadPayload struct {
|
|||
StackFileContent []byte
|
||||
EdgeGroups []portainer.EdgeGroupID
|
||||
// Deployment type to deploy this stack
|
||||
// Valid values are: 0 - 'compose', 1 - 'kubernetes', 2 - 'nomad'
|
||||
// for compose stacks will use kompose to convert to kubernetes manifest for kubernetes environments(endpoints)
|
||||
// kubernetes deploytype is enabled only for kubernetes environments(endpoints)
|
||||
// nomad deploytype is enabled only for nomad environments(endpoints)
|
||||
DeploymentType portainer.EdgeStackDeploymentType `example:"0" enums:"0,1,2"`
|
||||
// Valid values are: 0 - 'compose', 1 - 'kubernetes'
|
||||
// compose is enabled only for docker environments
|
||||
// kubernetes is enabled only for kubernetes environments
|
||||
DeploymentType portainer.EdgeStackDeploymentType `example:"0" enums:"0,1"`
|
||||
Registries []portainer.RegistryID
|
||||
// Uses the manifest's namespaces instead of the default one
|
||||
UseManifestNamespaces bool
|
||||
|
@ -49,6 +48,9 @@ func (payload *edgeStackFromFileUploadPayload) Validate(r *http.Request) error {
|
|||
return httperrors.NewInvalidPayloadError("Invalid deployment type")
|
||||
}
|
||||
payload.DeploymentType = portainer.EdgeStackDeploymentType(deploymentType)
|
||||
if payload.DeploymentType != portainer.EdgeStackDeploymentCompose && payload.DeploymentType != portainer.EdgeStackDeploymentKubernetes {
|
||||
return httperrors.NewInvalidPayloadError("Invalid deployment type")
|
||||
}
|
||||
|
||||
var registries []portainer.RegistryID
|
||||
err = request.RetrieveMultiPartFormJSONValue(r, "Registries", ®istries, true)
|
||||
|
|
|
@ -31,10 +31,9 @@ type edgeStackFromGitRepositoryPayload struct {
|
|||
// List of identifiers of EdgeGroups
|
||||
EdgeGroups []portainer.EdgeGroupID `example:"1"`
|
||||
// Deployment type to deploy this stack
|
||||
// Valid values are: 0 - 'compose', 1 - 'kubernetes', 2 - 'nomad'
|
||||
// for compose stacks will use kompose to convert to kubernetes manifest for kubernetes environments(endpoints)
|
||||
// kubernetes deploy type is enabled only for kubernetes environments(endpoints)
|
||||
// nomad deploy type is enabled only for nomad environments(endpoints)
|
||||
// Valid values are: 0 - 'compose', 1 - 'kubernetes'
|
||||
// compose is enabled only for docker environments
|
||||
// kubernetes is enabled only for kubernetes environments
|
||||
DeploymentType portainer.EdgeStackDeploymentType `example:"0" enums:"0,1,2"`
|
||||
// List of Registries to use for this stack
|
||||
Registries []portainer.RegistryID
|
||||
|
@ -48,12 +47,19 @@ func (payload *edgeStackFromGitRepositoryPayload) Validate(r *http.Request) erro
|
|||
if govalidator.IsNull(payload.Name) {
|
||||
return httperrors.NewInvalidPayloadError("Invalid stack name")
|
||||
}
|
||||
|
||||
if govalidator.IsNull(payload.RepositoryURL) || !govalidator.IsURL(payload.RepositoryURL) {
|
||||
return httperrors.NewInvalidPayloadError("Invalid repository URL. Must correspond to a valid URL format")
|
||||
}
|
||||
|
||||
if payload.RepositoryAuthentication && govalidator.IsNull(payload.RepositoryPassword) {
|
||||
return httperrors.NewInvalidPayloadError("Invalid repository credentials. Password must be specified when authentication is enabled")
|
||||
}
|
||||
|
||||
if payload.DeploymentType != portainer.EdgeStackDeploymentCompose && payload.DeploymentType != portainer.EdgeStackDeploymentKubernetes {
|
||||
return httperrors.NewInvalidPayloadError("Invalid deployment type")
|
||||
}
|
||||
|
||||
if govalidator.IsNull(payload.FilePathInRepository) {
|
||||
switch payload.DeploymentType {
|
||||
case portainer.EdgeStackDeploymentCompose:
|
||||
|
@ -62,9 +68,11 @@ func (payload *edgeStackFromGitRepositoryPayload) Validate(r *http.Request) erro
|
|||
payload.FilePathInRepository = filesystem.ManifestFileDefaultName
|
||||
}
|
||||
}
|
||||
|
||||
if len(payload.EdgeGroups) == 0 {
|
||||
return httperrors.NewInvalidPayloadError("Invalid edge groups. At least one edge group must be specified")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -119,6 +127,14 @@ func (handler *Handler) createEdgeStackFromGitRepository(r *http.Request, dryrun
|
|||
}
|
||||
|
||||
func (handler *Handler) storeManifestFromGitRepository(stackFolder string, relatedEndpointIds []portainer.EndpointID, deploymentType portainer.EdgeStackDeploymentType, currentUserID portainer.UserID, repositoryConfig gittypes.RepoConfig) (composePath, manifestPath, projectPath string, err error) {
|
||||
hasWrongType, err := hasWrongEnvironmentType(handler.DataStore.Endpoint(), relatedEndpointIds, deploymentType)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("unable to check for existence of non fitting environments: %w", err)
|
||||
}
|
||||
if hasWrongType {
|
||||
return "", "", "", fmt.Errorf("edge stack with config do not match the environment type")
|
||||
}
|
||||
|
||||
projectPath = handler.FileService.GetEdgeStackProjectPath(stackFolder)
|
||||
repositoryUsername := ""
|
||||
repositoryPassword := ""
|
||||
|
@ -133,14 +149,7 @@ func (handler *Handler) storeManifestFromGitRepository(stackFolder string, relat
|
|||
}
|
||||
|
||||
if deploymentType == portainer.EdgeStackDeploymentCompose {
|
||||
composePath := repositoryConfig.ConfigFilePath
|
||||
|
||||
manifestPath, err := handler.convertAndStoreKubeManifestIfNeeded(stackFolder, projectPath, composePath, relatedEndpointIds)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("Failed creating and storing kube manifest: %w", err)
|
||||
}
|
||||
|
||||
return composePath, manifestPath, projectPath, nil
|
||||
return repositoryConfig.ConfigFilePath, "", projectPath, nil
|
||||
}
|
||||
|
||||
if deploymentType == portainer.EdgeStackDeploymentKubernetes {
|
||||
|
|
|
@ -20,10 +20,9 @@ type edgeStackFromStringPayload struct {
|
|||
// List of identifiers of EdgeGroups
|
||||
EdgeGroups []portainer.EdgeGroupID `example:"1"`
|
||||
// Deployment type to deploy this stack
|
||||
// Valid values are: 0 - 'compose', 1 - 'kubernetes', 2 - 'nomad'
|
||||
// for compose stacks will use kompose to convert to kubernetes manifest for kubernetes environments(endpoints)
|
||||
// kubernetes deploy type is enabled only for kubernetes environments(endpoints)
|
||||
// nomad deploy type is enabled only for nomad environments(endpoints)
|
||||
// Valid values are: 0 - 'compose', 1 - 'kubernetes'
|
||||
// compose is enabled only for docker environments
|
||||
// kubernetes is enabled only for kubernetes environments
|
||||
DeploymentType portainer.EdgeStackDeploymentType `example:"0" enums:"0,1,2"`
|
||||
// List of Registries to use for this stack
|
||||
Registries []portainer.RegistryID
|
||||
|
@ -35,12 +34,19 @@ func (payload *edgeStackFromStringPayload) Validate(r *http.Request) error {
|
|||
if govalidator.IsNull(payload.Name) {
|
||||
return httperrors.NewInvalidPayloadError("Invalid stack name")
|
||||
}
|
||||
|
||||
if govalidator.IsNull(payload.StackFileContent) {
|
||||
return httperrors.NewInvalidPayloadError("Invalid stack file content")
|
||||
}
|
||||
|
||||
if len(payload.EdgeGroups) == 0 {
|
||||
return httperrors.NewInvalidPayloadError("Edge Groups are mandatory for an Edge stack")
|
||||
}
|
||||
|
||||
if payload.DeploymentType != portainer.EdgeStackDeploymentCompose && payload.DeploymentType != portainer.EdgeStackDeploymentKubernetes {
|
||||
return httperrors.NewInvalidPayloadError("Invalid deployment type")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -81,6 +87,14 @@ func (handler *Handler) createEdgeStackFromFileContent(r *http.Request, dryrun b
|
|||
}
|
||||
|
||||
func (handler *Handler) storeFileContent(stackFolder string, deploymentType portainer.EdgeStackDeploymentType, relatedEndpointIds []portainer.EndpointID, fileContent []byte) (composePath, manifestPath, projectPath string, err error) {
|
||||
hasWrongType, err := hasWrongEnvironmentType(handler.DataStore.Endpoint(), relatedEndpointIds, deploymentType)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("unable to check for existence of non fitting environments: %w", err)
|
||||
}
|
||||
if hasWrongType {
|
||||
return "", "", "", fmt.Errorf("edge stack with config do not match the environment type")
|
||||
}
|
||||
|
||||
if deploymentType == portainer.EdgeStackDeploymentCompose {
|
||||
composePath = filesystem.ComposeFileDefaultName
|
||||
|
||||
|
@ -89,22 +103,8 @@ func (handler *Handler) storeFileContent(stackFolder string, deploymentType port
|
|||
return "", "", "", err
|
||||
}
|
||||
|
||||
manifestPath, err = handler.convertAndStoreKubeManifestIfNeeded(stackFolder, projectPath, composePath, relatedEndpointIds)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("Failed creating and storing kube manifest: %w", err)
|
||||
}
|
||||
return composePath, "", projectPath, nil
|
||||
|
||||
return composePath, manifestPath, projectPath, nil
|
||||
|
||||
}
|
||||
|
||||
hasDockerEndpoint, err := hasDockerEndpoint(handler.DataStore.Endpoint(), relatedEndpointIds)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("unable to check for existence of docker environment: %w", err)
|
||||
}
|
||||
|
||||
if hasDockerEndpoint {
|
||||
return "", "", "", errors.New("edge stack with docker environment cannot be deployed with kubernetes or nomad config")
|
||||
}
|
||||
|
||||
if deploymentType == portainer.EdgeStackDeploymentKubernetes {
|
||||
|
|
|
@ -146,6 +146,14 @@ func (handler *Handler) edgeStackUpdate(w http.ResponseWriter, r *http.Request)
|
|||
|
||||
stackFolder := strconv.Itoa(int(stack.ID))
|
||||
|
||||
hasWrongType, err := hasWrongEnvironmentType(handler.DataStore.Endpoint(), relatedEndpointIds, payload.DeploymentType)
|
||||
if err != nil {
|
||||
return httperror.BadRequest("unable to check for existence of non fitting environments: %w", err)
|
||||
}
|
||||
if hasWrongType {
|
||||
return httperror.BadRequest("edge stack with config do not match the environment type", nil)
|
||||
}
|
||||
|
||||
if payload.DeploymentType == portainer.EdgeStackDeploymentCompose {
|
||||
if stack.EntryPoint == "" {
|
||||
stack.EntryPoint = filesystem.ComposeFileDefaultName
|
||||
|
@ -171,15 +179,6 @@ func (handler *Handler) edgeStackUpdate(w http.ResponseWriter, r *http.Request)
|
|||
|
||||
stack.UseManifestNamespaces = payload.UseManifestNamespaces
|
||||
|
||||
hasDockerEndpoint, err := hasDockerEndpoint(handler.DataStore.Endpoint(), relatedEndpointIds)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to check for existence of docker environment", err)
|
||||
}
|
||||
|
||||
if hasDockerEndpoint {
|
||||
return httperror.BadRequest("Edge stack with docker environment cannot be deployed with kubernetes config", err)
|
||||
}
|
||||
|
||||
_, err = handler.FileService.StoreEdgeStackFileFromBytes(stackFolder, stack.ManifestPath, []byte(payload.StackFileContent))
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to persist updated Kubernetes manifest file on disk", err)
|
||||
|
|
|
@ -30,3 +30,16 @@ func hasEndpointPredicate(endpointService dataservices.EndpointService, endpoint
|
|||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func hasWrongEnvironmentType(endpointService dataservices.EndpointService, endpointIDs []portainer.EndpointID, deploymentType portainer.EdgeStackDeploymentType) (bool, error) {
|
||||
return hasEndpointPredicate(endpointService, endpointIDs, func(e *portainer.Endpoint) bool {
|
||||
switch deploymentType {
|
||||
case portainer.EdgeStackDeploymentKubernetes:
|
||||
return !endpointutils.IsKubernetesEndpoint(e)
|
||||
case portainer.EdgeStackDeploymentCompose:
|
||||
return !endpointutils.IsDockerEndpoint(e)
|
||||
default:
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue