overlay.GetOverlay(name) returns now an error if the overlay doesn't exist. This is the most canonical way to act if there is no overlay.
158 lines
4.7 KiB
Go
158 lines
4.7 KiB
Go
package warewulfd
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/warewulf/warewulf/internal/pkg/config"
|
|
"github.com/warewulf/warewulf/internal/pkg/node"
|
|
"github.com/warewulf/warewulf/internal/pkg/overlay"
|
|
"github.com/warewulf/warewulf/internal/pkg/util"
|
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
|
)
|
|
|
|
func OverlaySend(w http.ResponseWriter, req *http.Request) {
|
|
rinfo, err := parseReqRender(req)
|
|
if err != nil {
|
|
message := "error parsing request: %s"
|
|
wwlog.ErrorExc(err, message, err)
|
|
http.Error(w, fmt.Sprintf(message, err), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
myOverlay, err := overlay.GetOverlay(rinfo.overlay)
|
|
if err != nil {
|
|
message := "overlay not found: %s"
|
|
wwlog.Error(message, rinfo.overlay)
|
|
http.Error(w, fmt.Sprintf(message, rinfo.overlay), http.StatusNoContent)
|
|
return
|
|
}
|
|
|
|
wwlog.Info("recv: render req overlay: %s, path: %s, node: %s", rinfo.overlay, rinfo.path, rinfo.node)
|
|
if config.Get().Warewulf.Secure() && rinfo.remoteport >= 1024 {
|
|
message := "non-privileged port: %s"
|
|
wwlog.Denied(message, req.RemoteAddr)
|
|
http.Error(w, fmt.Sprintf(message, req.RemoteAddr), http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
overlayFile := myOverlay.File(rinfo.path)
|
|
if !path.IsAbs(overlayFile) {
|
|
message := "Path %s isn't absolute"
|
|
wwlog.Denied(message, overlayFile)
|
|
http.Error(w, fmt.Sprintf(message, overlayFile), http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
if !util.IsFile(overlayFile) {
|
|
if rinfo.node != "" && util.IsFile(overlayFile+".ww") {
|
|
wwlog.Debug("appending .ww for file: %s", overlayFile)
|
|
overlayFile += ".ww"
|
|
} else {
|
|
message := "file doesn't exists: %s"
|
|
wwlog.Denied(message, overlayFile)
|
|
http.Error(w, fmt.Sprintf(message, overlayFile), http.StatusNotFound)
|
|
return
|
|
}
|
|
}
|
|
|
|
if strings.HasSuffix(overlayFile, ".ww") && rinfo.node != "" {
|
|
nodeDB, err := node.New()
|
|
if err != nil {
|
|
message := "error opening node database: %s"
|
|
wwlog.ErrorExc(err, message, err)
|
|
http.Error(w, fmt.Sprintf(message, err), http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
node, err := nodeDB.GetNode(rinfo.node)
|
|
if err != nil {
|
|
message := "error getting node: %s"
|
|
wwlog.ErrorExc(err, message, err)
|
|
http.Error(w, fmt.Sprintf(message, err), http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
allNodes, err := nodeDB.FindAllNodes()
|
|
if err != nil {
|
|
message := "error loading nodes from registry: %s"
|
|
wwlog.ErrorExc(err, message, err)
|
|
http.Error(w, fmt.Sprintf(message, err), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
tstruct, err := overlay.InitStruct(overlayFile, node, allNodes)
|
|
if err != nil {
|
|
message := "error initializing template data: %s"
|
|
wwlog.ErrorExc(err, message, err)
|
|
http.Error(w, fmt.Sprintf(message, err), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
tstruct.BuildSource = overlayFile
|
|
|
|
buffer, _, _, err := overlay.RenderTemplateFile(overlayFile, tstruct)
|
|
if err != nil {
|
|
message := "error rendering overlay template: %s"
|
|
wwlog.ErrorExc(err, message, err)
|
|
http.Error(w, fmt.Sprintf(message, err), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Length", strconv.Itoa(buffer.Len()))
|
|
_, err = buffer.WriteTo(w)
|
|
if err != nil {
|
|
message := "error writing overlay template over http connection: %s"
|
|
wwlog.ErrorExc(err, message, err)
|
|
http.Error(w, fmt.Sprintf(message, err), http.StatusInternalServerError)
|
|
}
|
|
wwlog.Info("%s: %s", node.Id(), overlayFile)
|
|
} else {
|
|
fileBytes, err := os.ReadFile(overlayFile)
|
|
if err != nil {
|
|
message := "error reading file: %s"
|
|
wwlog.ErrorExc(err, message, err)
|
|
http.Error(w, fmt.Sprintf(message, err), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
_, err = w.Write(fileBytes)
|
|
if err != nil {
|
|
message := "error writing overlay file over http connection: %s"
|
|
wwlog.ErrorExc(err, message, err)
|
|
http.Error(w, fmt.Sprintf(message, err), http.StatusInternalServerError)
|
|
}
|
|
wwlog.Info("send overlay file for node %s: %s", rinfo.node, overlayFile)
|
|
}
|
|
}
|
|
|
|
type parserInfoRender struct {
|
|
overlay string
|
|
path string
|
|
node string
|
|
remoteport int
|
|
}
|
|
|
|
func parseReqRender(req *http.Request) (ret parserInfoRender, err error) {
|
|
parts := strings.Split(req.URL.Path, "/")
|
|
ret.overlay = parts[2]
|
|
if ret.overlay == "" {
|
|
return ret, fmt.Errorf("no overlay specified")
|
|
}
|
|
ret.path = strings.Join(parts[3:], "/")
|
|
if ret.path == "" {
|
|
return ret, fmt.Errorf("no path specified")
|
|
}
|
|
if len(req.URL.Query()["render"]) > 0 {
|
|
ret.node = req.URL.Query()["render"][0]
|
|
}
|
|
if _, remoteport, err := net.SplitHostPort(req.RemoteAddr); err != nil {
|
|
return ret, fmt.Errorf("could not obtain remote port from HTTP request: %w", err)
|
|
} else if ret.remoteport, err = strconv.Atoi(remoteport); err != nil {
|
|
return ret, fmt.Errorf("couldn't obtain remote port from HTTP request: %w", err)
|
|
}
|
|
return ret, nil
|
|
}
|