Rename selected configuration profiles to overlays
This commit is contained in:
131
internal/engine/selected_overlay.go
Normal file
131
internal/engine/selected_overlay.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
yamladapter "gitea.maximumdirect.net/eric/ConfigKit/internal/yaml"
|
||||
)
|
||||
|
||||
func activeOverlay(name string) bool {
|
||||
return name != "" && name != "default"
|
||||
}
|
||||
|
||||
func validOverlayName(name string) bool {
|
||||
if !activeOverlay(name) {
|
||||
return true
|
||||
}
|
||||
for _, character := range name {
|
||||
if (character >= 'a' && character <= 'z') ||
|
||||
(character >= 'A' && character <= 'Z') ||
|
||||
(character >= '0' && character <= '9') || character == '_' || character == '-' {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func loadSelectedOverlayDocument(source Source, context *FilesystemContext, main parsedSource, base *composedDocument, sources []SourceInfo, name string, rule *VersionRule, limits importLimits) (*composedDocument, []SourceInfo, []Diagnostic) {
|
||||
rootPath, _, _, diagnostic := importFilesystem(source, context, main)
|
||||
if diagnostic != nil {
|
||||
return nil, cloneSourceInfos(sources), []Diagnostic{*diagnostic}
|
||||
}
|
||||
overlayPath := filepath.Join(rootPath, "overlays", name+".yml")
|
||||
canonical, err := canonicalExistingPath(overlayPath)
|
||||
if err != nil {
|
||||
code := CodeOverlayInvalid
|
||||
message := "selected overlay could not be resolved"
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
code, message = CodeOverlayMissing, "selected overlay does not exist"
|
||||
} else if errors.Is(err, fs.ErrPermission) {
|
||||
code, message = CodeImportPermission, "selected overlay cannot be accessed"
|
||||
}
|
||||
return nil, cloneSourceInfos(sources), []Diagnostic{importDiagnostic(code, message, nil)}
|
||||
}
|
||||
if !containedPath(rootPath, canonical) {
|
||||
return nil, cloneSourceInfos(sources), []Diagnostic{importDiagnostic(CodeOverlayOutsideRoot, "selected overlay resolves outside the configuration root", nil)}
|
||||
}
|
||||
for _, existing := range sources {
|
||||
if existing.Path != "" && existing.Path == canonical {
|
||||
return nil, cloneSourceInfos(sources), []Diagnostic{importDiagnostic(CodeOverlayRepeated, "selected overlay is already present in the base source graph", nil)}
|
||||
}
|
||||
}
|
||||
if len(sources)+1 > limits.sources {
|
||||
return nil, cloneSourceInfos(sources), []Diagnostic{importDiagnostic(CodeOverlayLimitExceeded, "selected overlay exceeds the source-count limit", nil)}
|
||||
}
|
||||
|
||||
root, err := os.OpenRoot(rootPath)
|
||||
if err != nil {
|
||||
value := globFilesystemDiagnostic(err, "configuration root could not be opened for the selected overlay", Location{})
|
||||
value.Primary = nil
|
||||
return nil, cloneSourceInfos(sources), []Diagnostic{value}
|
||||
}
|
||||
rootClosed := false
|
||||
defer func() {
|
||||
if !rootClosed {
|
||||
_ = root.Close()
|
||||
}
|
||||
}()
|
||||
relative, err := filepath.Rel(rootPath, canonical)
|
||||
if err != nil || filepath.IsAbs(relative) || relative == ".." || strings.HasPrefix(relative, ".."+string(filepath.Separator)) {
|
||||
return nil, cloneSourceInfos(sources), []Diagnostic{importDiagnostic(CodeOverlayOutsideRoot, "selected overlay could not be addressed inside the configuration root", nil)}
|
||||
}
|
||||
|
||||
sourceID := "source-" + strconv.Itoa(len(sources)+1)
|
||||
overlayLocation := &Location{SourceID: sourceID}
|
||||
overlaySource := SourceInfo{ID: sourceID, Label: filepath.ToSlash(filepath.Join("overlays", name+".yml")), Path: canonical, Layer: LayerOverlay}
|
||||
sources = append(cloneSourceInfos(sources), overlaySource)
|
||||
data, diagnostic := readOverlayFile(root, relative, sourceID, limits.bytes-base.inputBytes)
|
||||
if diagnostic != nil {
|
||||
return nil, sources, []Diagnostic{*diagnostic}
|
||||
}
|
||||
overlayRoot, issues := yamladapter.Parse(bytes.NewReader(data), sourceID, yamladapter.Limits{Depth: maxDocumentDepth, Nodes: maxDocumentNodes})
|
||||
if len(issues) != 0 {
|
||||
return nil, sources, parserDiagnostics(sourceID, issues)
|
||||
}
|
||||
nodeCount := countInputNodes(overlayRoot)
|
||||
if base.inputNodes+nodeCount > limits.nodes {
|
||||
return nil, sources, []Diagnostic{importDiagnostic(CodeOverlayLimitExceeded, "selected overlay exceeds the aggregate node limit", overlayLocation)}
|
||||
}
|
||||
if diagnostics := overlayDirectiveDiagnostics(overlayRoot, Path{}); len(diagnostics) != 0 {
|
||||
return nil, sources, diagnostics
|
||||
}
|
||||
if versionNode := fragmentVersionClaim(overlayRoot, rule); versionNode != nil {
|
||||
location := publicLocation(versionNode.Location)
|
||||
return nil, sources, []Diagnostic{{
|
||||
Code: CodeOverlayVersion, Severity: SeverityError, Path: rule.Path.clone(),
|
||||
Message: "overlays cannot contribute to or replace the schema-version path", Primary: &location,
|
||||
}}
|
||||
}
|
||||
closeErr := root.Close()
|
||||
rootClosed = true
|
||||
if closeErr != nil {
|
||||
return nil, sources, []Diagnostic{importDiagnostic(CodeImportRead, "configuration root could not be closed after reading the selected overlay", nil)}
|
||||
}
|
||||
|
||||
overlayd, diagnostics := overlayDocuments(base, overlayRoot)
|
||||
if overlayd != nil {
|
||||
overlayd.inputBytes = base.inputBytes + int64(len(data))
|
||||
overlayd.inputNodes = base.inputNodes + nodeCount
|
||||
}
|
||||
return overlayd, sources, diagnostics
|
||||
}
|
||||
|
||||
func readOverlayFile(root *os.Root, relative, sourceID string, remaining int64) ([]byte, *Diagnostic) {
|
||||
limit := min(int64(maxSourceBytes), remaining)
|
||||
if limit < 0 {
|
||||
limit = 0
|
||||
}
|
||||
data, failure := readRegularFile(relative, root.Stat, root.Open, limit)
|
||||
if failure != nil {
|
||||
value := sourceReadDiagnostic(failure, sourceID, CodeOverlayLimitExceeded, "selected overlay exceeds a source or aggregate byte limit")
|
||||
return nil, &value
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
Reference in New Issue
Block a user