Skip to content

Commit

Permalink
Extract DV schema preprocessing
Browse files Browse the repository at this point in the history
- schema inputs are processed nearly identically to data value inputs;
  mirroring the structure conveys this symmetry.
  • Loading branch information
jtigger committed Dec 24, 2021
1 parent 2e961ae commit 8e6cf37
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 74 deletions.
151 changes: 151 additions & 0 deletions pkg/workspace/data_values_schema_pre_processing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright 2020 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0

package workspace

import (
"fmt"
"strings"

"github.com/k14s/starlark-go/starlark"
"github.com/k14s/ytt/pkg/workspace/datavalues"
"github.com/k14s/ytt/pkg/yamlmeta"
yttoverlay "github.com/k14s/ytt/pkg/yttlibrary/overlay"
)

// DataValuesSchemaPreProcessing combines all data values schema documents (and any overlays) into a result set.
type DataValuesSchemaPreProcessing struct {
schemaFiles []*FileInLibrary
schemaOverlays []*datavalues.SchemaEnvelope
loader *TemplateLoader
}

// Apply executes the pre-processing of schema for data values for all libraries.
//
// Returns the schema for the root library and enveloped schema for children libraries.
func (pp DataValuesSchemaPreProcessing) Apply() (*datavalues.Schema, []*datavalues.SchemaEnvelope, error) {
files := append([]*FileInLibrary{}, pp.schemaFiles...)

// Ensure files are in assigned order so that overlays will be applied correctly.
SortFilesInLibrary(files)

schema, libSchemas, err := pp.apply(files)
if err != nil {
errMsg := "Overlaying data values schema (in following order: %s): %s"
return nil, nil, fmt.Errorf(errMsg, pp.allFileDescs(files), err)
}

return schema, libSchemas, nil
}

func (pp DataValuesSchemaPreProcessing) apply(files []*FileInLibrary) (*datavalues.Schema, []*datavalues.SchemaEnvelope, error) {
allSchemas, err := pp.collectSchemaDocs(files)
if err != nil {
return nil, nil, err
}

// merge all Schema documents into one
var schemaDoc *yamlmeta.Document
var childLibSchemas []*datavalues.SchemaEnvelope
for _, schema := range allSchemas {
if schema.IntendedForAnotherLibrary() {
childLibSchemas = append(childLibSchemas, schema)
continue
}

if schemaDoc == nil {
schemaDoc = schema.Source()
} else {
schemaDoc, err = pp.overlay(schemaDoc, schema.Source())
if err != nil {
return nil, nil, err
}
}
}

var schema *datavalues.Schema
if schemaDoc == nil {
schema = datavalues.NewNullSchema()
} else {
schema, err = datavalues.NewSchema(schemaDoc)
if err != nil {
return nil, nil, err
}
}

return schema, childLibSchemas, nil
}

func (pp DataValuesSchemaPreProcessing) collectSchemaDocs(schemaFiles []*FileInLibrary) ([]*datavalues.SchemaEnvelope, error) {
var allSchema []*datavalues.SchemaEnvelope
for _, file := range schemaFiles {
docs, err := pp.extractSchemaDocs(file)
if err != nil {
return nil, fmt.Errorf("Templating file '%s': %s", file.File.RelativePath(), err)
}

for _, d := range docs {
s, err := datavalues.NewSchemaEnvelope(d)
if err != nil {
return nil, err
}
allSchema = append(allSchema, s)
}
}
allSchema = append(allSchema, pp.schemaOverlays...)
return allSchema, nil
}

func (pp DataValuesSchemaPreProcessing) extractSchemaDocs(schemaFile *FileInLibrary) ([]*yamlmeta.Document, error) {
libraryCtx := LibraryExecutionContext{Current: schemaFile.Library, Root: NewRootLibrary(nil)}

_, resultDocSet, err := pp.loader.EvalYAML(libraryCtx, schemaFile.File)
if err != nil {
return nil, err
}

schemaDocs, nonSchemaDocs, err := DocExtractor{resultDocSet}.Extract(datavalues.AnnotationDataValuesSchema)
if err != nil {
return nil, err
}

// For simplicity's sake, prohibit mixing data value schema documents with other kinds.
if len(nonSchemaDocs) > 0 {
for _, doc := range nonSchemaDocs {
if !doc.IsEmpty() {
errStr := "Expected schema file '%s' to only have schema documents"
return nil, fmt.Errorf(errStr, schemaFile.File.RelativePath())
}
}
}

return schemaDocs, nil
}

func (pp DataValuesSchemaPreProcessing) allFileDescs(files []*FileInLibrary) string {
var result []string
for _, f := range files {
result = append(result, f.File.RelativePath())
}
if len(pp.schemaOverlays) > 0 {
result = append(result, "additional data value schema")
}
return strings.Join(result, ", ")
}

func (pp DataValuesSchemaPreProcessing) overlay(doc, overlay *yamlmeta.Document) (*yamlmeta.Document, error) {
op := yttoverlay.Op{
Left: &yamlmeta.DocumentSet{Items: []*yamlmeta.Document{doc}},
Right: &yamlmeta.DocumentSet{Items: []*yamlmeta.Document{overlay}},
Thread: &starlark.Thread{Name: "data-values-schema-pre-processing"},

ExactMatch: true,
}

result, err := op.Apply()
if err != nil {
return nil, err
}

return result.(*yamlmeta.DocumentSet).Items[0], nil
}
81 changes: 7 additions & 74 deletions pkg/workspace/library_execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/k14s/ytt/pkg/template"
"github.com/k14s/ytt/pkg/workspace/datavalues"
"github.com/k14s/ytt/pkg/yamlmeta"
yttoverlay "github.com/k14s/ytt/pkg/yttlibrary/overlay"
)

type LibraryExecution struct {
Expand Down Expand Up @@ -50,87 +49,21 @@ func NewLibraryExecution(libraryCtx LibraryExecutionContext,
// in the library and the passed-in overlays.
//
// Returns this library's Schema and a slice of Schema intended for child libraries.
func (ll *LibraryExecution) Schemas(schemaOverlays []*datavalues.SchemaEnvelope) (*datavalues.Schema, []*datavalues.SchemaEnvelope, error) {
func (ll *LibraryExecution) Schemas(overlays []*datavalues.SchemaEnvelope) (*datavalues.Schema, []*datavalues.SchemaEnvelope, error) {
loader := NewTemplateLoader(datavalues.NewEmptyEnvelope(), nil, nil, ll.templateLoaderOpts, ll.libraryExecFactory, ll.ui)

schemaFiles, err := ll.schemaFiles(loader)
files, err := ll.schemaFiles(loader)
if err != nil {
return nil, nil, err
}

documentSchemas, err := collectSchemaDocs(schemaFiles, loader)
if err != nil {
return nil, nil, err
}

documentSchemas = append(documentSchemas, schemaOverlays...)

var resultSchemasDoc *yamlmeta.Document
var childLibrarySchemas []*datavalues.SchemaEnvelope
for _, docSchema := range documentSchemas {
if docSchema.IntendedForAnotherLibrary() {
childLibrarySchemas = append(childLibrarySchemas, docSchema)
continue
}
if resultSchemasDoc == nil {
resultSchemasDoc = docSchema.Source()
} else {
resultSchemasDoc, err = ll.overlay(resultSchemasDoc, docSchema.Source())
if err != nil {
return nil, nil, err
}
}
}
if resultSchemasDoc != nil {
currentLibrarySchema, err := datavalues.NewSchema(resultSchemasDoc)
if err != nil {
return nil, nil, err
}
return currentLibrarySchema, childLibrarySchemas, nil
}
return datavalues.NewNullSchema(), childLibrarySchemas, nil
}

func collectSchemaDocs(schemaFiles []*FileInLibrary, loader *TemplateLoader) ([]*datavalues.SchemaEnvelope, error) {
var documentSchemas []*datavalues.SchemaEnvelope
for _, file := range schemaFiles {
libraryCtx := LibraryExecutionContext{Current: file.Library, Root: NewRootLibrary(nil)}

_, resultDocSet, err := loader.EvalYAML(libraryCtx, file.File)
if err != nil {
return nil, err
}

docs, _, err := DocExtractor{resultDocSet}.Extract(datavalues.AnnotationDataValuesSchema)
if err != nil {
return nil, err
}
for _, doc := range docs {
newSchema, err := datavalues.NewSchemaEnvelope(doc)
if err != nil {
return nil, err
}
documentSchemas = append(documentSchemas, newSchema)
}
}
return documentSchemas, nil
}

func (ll *LibraryExecution) overlay(schema, overlay *yamlmeta.Document) (*yamlmeta.Document, error) {
op := yttoverlay.Op{
Left: &yamlmeta.DocumentSet{Items: []*yamlmeta.Document{schema}},
Right: &yamlmeta.DocumentSet{Items: []*yamlmeta.Document{overlay}},
Thread: &starlark.Thread{Name: "schema-pre-processing"},

ExactMatch: true,
}

newLeft, err := op.Apply()
if err != nil {
return nil, err
spp := DataValuesSchemaPreProcessing{
schemaFiles: files,
schemaOverlays: overlays,
loader: loader,
}

return newLeft.(*yamlmeta.DocumentSet).Items[0], nil
return spp.Apply()
}

// Values calculates the final Data Values for this library by combining/overlaying defaults from the schema, the Data
Expand Down

0 comments on commit 8e6cf37

Please sign in to comment.