portainer/portainer

View on GitHub
api/http/handler/templates/template_file.go

Summary

Maintainability
A
1 hr
Test Coverage
package templates

import (
    "net/http"
    "slices"

    portainer "github.com/portainer/portainer/api"
    httperror "github.com/portainer/portainer/pkg/libhttp/error"
    "github.com/portainer/portainer/pkg/libhttp/request"
    "github.com/portainer/portainer/pkg/libhttp/response"

    "github.com/rs/zerolog/log"
)

type fileResponse struct {
    // The requested file content
    FileContent string `example:"version:2"`
}

// @id TemplateFile
// @summary Get a template's file
// @description Get a template's file
// @description **Access policy**: authenticated
// @tags templates
// @security ApiKeyAuth
// @security jwt
// @accept json
// @produce json
// @param id path int true "Template identifier"
// @success 200 {object} fileResponse "Success"
// @failure 400 "Invalid request"
// @failure 500 "Server error"
// @router /templates/{id}/file [post]
func (handler *Handler) templateFile(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
    id, err := request.RetrieveNumericRouteVariableValue(r, "id")
    if err != nil {
        return httperror.BadRequest("Invalid template identifier", err)
    }

    templatesResponse, httpErr := handler.fetchTemplates()
    if httpErr != nil {
        return httpErr
    }

    templateIdx := slices.IndexFunc(templatesResponse.Templates, func(template portainer.Template) bool {
        return template.ID == portainer.TemplateID(id)
    })

    if templateIdx == -1 {
        return httperror.NotFound("Unable to find a template with the specified identifier", nil)
    }

    template := templatesResponse.Templates[templateIdx]

    if template.Type == portainer.ContainerTemplate {
        return httperror.BadRequest("Invalid template type", nil)
    }

    if template.StackFile != "" {
        return response.JSON(w, fileResponse{FileContent: template.StackFile})
    }

    if template.Repository.StackFile == "" || template.Repository.URL == "" {
        return httperror.BadRequest("Invalid template configuration", nil)
    }

    projectPath, err := handler.FileService.GetTemporaryPath()
    if err != nil {
        return httperror.InternalServerError("Unable to create temporary folder", err)
    }

    defer handler.cleanUp(projectPath)

    err = handler.GitService.CloneRepository(projectPath, template.Repository.URL, "", "", "", false)
    if err != nil {
        return httperror.InternalServerError("Unable to clone git repository", err)
    }

    fileContent, err := handler.FileService.GetFileContent(projectPath, template.Repository.StackFile)
    if err != nil {
        return httperror.InternalServerError("Failed loading file content", err)
    }

    return response.JSON(w, fileResponse{FileContent: string(fileContent)})

}

func (handler *Handler) cleanUp(projectPath string) {
    err := handler.FileService.RemoveDirectory(projectPath)
    if err != nil {
        log.Debug().Err(err).Msg("HTTP error: unable to cleanup stack creation")
    }
}