78
vendor/sigs.k8s.io/kustomize/kyaml/comments/comments.go
generated
vendored
Normal file
78
vendor/sigs.k8s.io/kustomize/kyaml/comments/comments.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package comments
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml/walk"
|
||||
)
|
||||
|
||||
// CopyComments recursively copies the comments on fields in from to fields in to
|
||||
func CopyComments(from, to *yaml.RNode) error {
|
||||
copy(from, to)
|
||||
// walk the fields copying comments
|
||||
_, err := walk.Walker{
|
||||
Sources: []*yaml.RNode{from, to},
|
||||
Visitor: &copier{},
|
||||
VisitKeysAsScalars: true}.Walk()
|
||||
return err
|
||||
}
|
||||
|
||||
// copier implements walk.Visitor, and copies comments to fields shared between 2 instances
|
||||
// of a resource
|
||||
type copier struct{}
|
||||
|
||||
func (c *copier) VisitMap(s walk.Sources, _ *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
copy(s.Dest(), s.Origin())
|
||||
return s.Dest(), nil
|
||||
}
|
||||
|
||||
func (c *copier) VisitScalar(s walk.Sources, _ *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
to := s.Origin()
|
||||
// TODO: File a bug with upstream yaml to handle comments for FoldedStyle scalar nodes
|
||||
// Hack: convert FoldedStyle scalar node to DoubleQuotedStyle as the line comments are
|
||||
// being serialized without space
|
||||
// https://github.com/GoogleContainerTools/kpt/issues/766
|
||||
if to != nil && to.Document().Style == yaml.FoldedStyle {
|
||||
to.Document().Style = yaml.DoubleQuotedStyle
|
||||
}
|
||||
|
||||
copy(s.Dest(), to)
|
||||
return s.Dest(), nil
|
||||
}
|
||||
|
||||
func (c *copier) VisitList(s walk.Sources, _ *openapi.ResourceSchema, _ walk.ListKind) (
|
||||
*yaml.RNode, error) {
|
||||
copy(s.Dest(), s.Origin())
|
||||
destItems := s.Dest().Content()
|
||||
originItems := s.Origin().Content()
|
||||
|
||||
for i := 0; i < len(destItems) && i < len(originItems); i++ {
|
||||
dest := destItems[i]
|
||||
origin := originItems[i]
|
||||
|
||||
if dest.Value == origin.Value {
|
||||
copy(yaml.NewRNode(dest), yaml.NewRNode(origin))
|
||||
}
|
||||
}
|
||||
|
||||
return s.Dest(), nil
|
||||
}
|
||||
|
||||
// copy copies the comment from one field to another
|
||||
func copy(from, to *yaml.RNode) {
|
||||
if from == nil || to == nil {
|
||||
return
|
||||
}
|
||||
if to.Document().LineComment == "" {
|
||||
to.Document().LineComment = from.Document().LineComment
|
||||
}
|
||||
if to.Document().HeadComment == "" {
|
||||
to.Document().HeadComment = from.Document().HeadComment
|
||||
}
|
||||
if to.Document().FootComment == "" {
|
||||
to.Document().FootComment = from.Document().FootComment
|
||||
}
|
||||
}
|
||||
10
vendor/sigs.k8s.io/kustomize/kyaml/ext/ext.go
generated
vendored
Normal file
10
vendor/sigs.k8s.io/kustomize/kyaml/ext/ext.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package ext
|
||||
|
||||
// IgnoreFileName returns the name for ignore files in
|
||||
// packages. It can be overridden by tools using this library.
|
||||
var IgnoreFileName = func() string {
|
||||
return ".krmignore"
|
||||
}
|
||||
275
vendor/sigs.k8s.io/kustomize/kyaml/fieldmeta/fieldmeta.go
generated
vendored
Normal file
275
vendor/sigs.k8s.io/kustomize/kyaml/fieldmeta/fieldmeta.go
generated
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package fieldmeta
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// FieldMeta contains metadata that may be attached to fields as comments
|
||||
type FieldMeta struct {
|
||||
Schema spec.Schema
|
||||
|
||||
Extensions XKustomize
|
||||
|
||||
SettersSchema *spec.Schema
|
||||
}
|
||||
|
||||
type XKustomize struct {
|
||||
SetBy string `yaml:"setBy,omitempty" json:"setBy,omitempty"`
|
||||
PartialFieldSetters []PartialFieldSetter `yaml:"partialSetters,omitempty" json:"partialSetters,omitempty"`
|
||||
FieldSetter *PartialFieldSetter `yaml:"setter,omitempty" json:"setter,omitempty"`
|
||||
}
|
||||
|
||||
// PartialFieldSetter defines how to set part of a field rather than the full field
|
||||
// value. e.g. the tag part of an image field
|
||||
type PartialFieldSetter struct {
|
||||
// Name is the name of this setter.
|
||||
Name string `yaml:"name" json:"name"`
|
||||
|
||||
// Value is the current value that has been set.
|
||||
Value string `yaml:"value" json:"value"`
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the FieldMeta has any empty Schema
|
||||
func (fm *FieldMeta) IsEmpty() bool {
|
||||
if fm == nil {
|
||||
return true
|
||||
}
|
||||
return reflect.DeepEqual(fm.Schema, spec.Schema{})
|
||||
}
|
||||
|
||||
// Read reads the FieldMeta from a node
|
||||
func (fm *FieldMeta) Read(n *yaml.RNode) error {
|
||||
// check for metadata on head and line comments
|
||||
comments := []string{n.YNode().LineComment, n.YNode().HeadComment}
|
||||
for _, c := range comments {
|
||||
if c == "" {
|
||||
continue
|
||||
}
|
||||
c := strings.TrimLeft(c, "#")
|
||||
|
||||
// check for new short hand notation or fall back to openAPI ref format
|
||||
if !fm.processShortHand(c) {
|
||||
// if it doesn't Unmarshal that is fine, it means there is no metadata
|
||||
// other comments are valid, they just don't parse
|
||||
// TODO: consider more sophisticated parsing techniques similar to what is used
|
||||
// for go struct tags.
|
||||
if err := fm.Schema.UnmarshalJSON([]byte(c)); err != nil {
|
||||
// note: don't return an error if the comment isn't a fieldmeta struct
|
||||
return nil
|
||||
}
|
||||
}
|
||||
fe := fm.Schema.VendorExtensible.Extensions["x-kustomize"]
|
||||
if fe == nil {
|
||||
return nil
|
||||
}
|
||||
b, err := json.Marshal(fe)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
return json.Unmarshal(b, &fm.Extensions)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// processShortHand parses the comment for short hand ref, loads schema to fm
|
||||
// and returns true if successful, returns false for any other cases and not throw
|
||||
// error, as the comment might not be a setter ref
|
||||
func (fm *FieldMeta) processShortHand(comment string) bool {
|
||||
input := map[string]string{}
|
||||
err := json.Unmarshal([]byte(comment), &input)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
name := input[shortHandRef]
|
||||
if name == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// check if setter with the name exists, else check for a substitution
|
||||
// setter and substitution can't have same name in shorthand
|
||||
|
||||
setterRef, err := spec.NewRef(DefinitionsPrefix + SetterDefinitionPrefix + name)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
setterRefBytes, err := setterRef.MarshalJSON()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if _, err := openapi.Resolve(&setterRef, fm.SettersSchema); err == nil {
|
||||
setterErr := fm.Schema.UnmarshalJSON(setterRefBytes)
|
||||
return setterErr == nil
|
||||
}
|
||||
|
||||
substRef, err := spec.NewRef(DefinitionsPrefix + SubstitutionDefinitionPrefix + name)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
substRefBytes, err := substRef.MarshalJSON()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if _, err := openapi.Resolve(&substRef, fm.SettersSchema); err == nil {
|
||||
substErr := fm.Schema.UnmarshalJSON(substRefBytes)
|
||||
return substErr == nil
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isExtensionEmpty(x XKustomize) bool {
|
||||
if x.FieldSetter != nil {
|
||||
return false
|
||||
}
|
||||
if x.SetBy != "" {
|
||||
return false
|
||||
}
|
||||
if len(x.PartialFieldSetters) > 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Write writes the FieldMeta to a node
|
||||
func (fm *FieldMeta) Write(n *yaml.RNode) error {
|
||||
if !isExtensionEmpty(fm.Extensions) {
|
||||
return fm.WriteV1Setters(n)
|
||||
}
|
||||
|
||||
// Ref is removed when a setter is deleted, so the Ref string could be empty.
|
||||
if fm.Schema.Ref.String() != "" {
|
||||
// Ex: {"$ref":"#/definitions/io.k8s.cli.setters.replicas"} should be converted to
|
||||
// {"$openAPI":"replicas"} and added to the line comment
|
||||
ref := fm.Schema.Ref.String()
|
||||
var shortHandRefValue string
|
||||
switch {
|
||||
case strings.HasPrefix(ref, DefinitionsPrefix+SetterDefinitionPrefix):
|
||||
shortHandRefValue = strings.TrimPrefix(ref, DefinitionsPrefix+SetterDefinitionPrefix)
|
||||
case strings.HasPrefix(ref, DefinitionsPrefix+SubstitutionDefinitionPrefix):
|
||||
shortHandRefValue = strings.TrimPrefix(ref, DefinitionsPrefix+SubstitutionDefinitionPrefix)
|
||||
default:
|
||||
return fmt.Errorf("unexpected ref format: %s", ref)
|
||||
}
|
||||
n.YNode().LineComment = fmt.Sprintf(`{"%s":"%s"}`, shortHandRef,
|
||||
shortHandRefValue)
|
||||
} else {
|
||||
n.YNode().LineComment = ""
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteV1Setters is the v1 setters way of writing setter definitions
|
||||
// TODO: pmarupaka - remove this method after migration
|
||||
func (fm *FieldMeta) WriteV1Setters(n *yaml.RNode) error {
|
||||
fm.Schema.VendorExtensible.AddExtension("x-kustomize", fm.Extensions)
|
||||
b, err := json.Marshal(fm.Schema)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
n.YNode().LineComment = string(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
// FieldValueType defines the type of input to register
|
||||
type FieldValueType string
|
||||
|
||||
const (
|
||||
// String defines a string flag
|
||||
String FieldValueType = "string"
|
||||
// Bool defines a bool flag
|
||||
Bool = "boolean"
|
||||
// Int defines an int flag
|
||||
Int = "integer"
|
||||
)
|
||||
|
||||
func (it FieldValueType) String() string {
|
||||
if it == "" {
|
||||
return "string"
|
||||
}
|
||||
return string(it)
|
||||
}
|
||||
|
||||
func (it FieldValueType) Validate(value string) error {
|
||||
switch it {
|
||||
case Int:
|
||||
if _, err := strconv.Atoi(value); err != nil {
|
||||
return errors.WrapPrefixf(err, "value must be an int")
|
||||
}
|
||||
case Bool:
|
||||
if _, err := strconv.ParseBool(value); err != nil {
|
||||
return errors.WrapPrefixf(err, "value must be a bool")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (it FieldValueType) Tag() string {
|
||||
switch it {
|
||||
case String:
|
||||
return yaml.NodeTagString
|
||||
case Bool:
|
||||
return yaml.NodeTagBool
|
||||
case Int:
|
||||
return yaml.NodeTagInt
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (it FieldValueType) TagForValue(value string) string {
|
||||
switch it {
|
||||
case String:
|
||||
return yaml.NodeTagString
|
||||
case Bool:
|
||||
if _, err := strconv.ParseBool(string(it)); err != nil {
|
||||
return ""
|
||||
}
|
||||
return yaml.NodeTagBool
|
||||
case Int:
|
||||
if _, err := strconv.ParseInt(string(it), 0, 32); err != nil {
|
||||
return ""
|
||||
}
|
||||
return yaml.NodeTagInt
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
const (
|
||||
// CLIDefinitionsPrefix is the prefix for cli definition keys.
|
||||
CLIDefinitionsPrefix = "io.k8s.cli."
|
||||
|
||||
// SetterDefinitionPrefix is the prefix for setter definition keys.
|
||||
SetterDefinitionPrefix = CLIDefinitionsPrefix + "setters."
|
||||
|
||||
// SubstitutionDefinitionPrefix is the prefix for substitution definition keys.
|
||||
SubstitutionDefinitionPrefix = CLIDefinitionsPrefix + "substitutions."
|
||||
|
||||
// DefinitionsPrefix is the prefix used to reference definitions in the OpenAPI
|
||||
DefinitionsPrefix = "#/definitions/"
|
||||
)
|
||||
|
||||
// shortHandRef is the shorthand reference to setters and substitutions
|
||||
var shortHandRef = "$openapi"
|
||||
|
||||
func SetShortHandRef(ref string) {
|
||||
shortHandRef = ref
|
||||
}
|
||||
|
||||
func ShortHandRef() string {
|
||||
return shortHandRef
|
||||
}
|
||||
192
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/container/container.go
generated
vendored
Normal file
192
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/container/container.go
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
runtimeexec "sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
|
||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Filter filters Resources using a container image.
|
||||
// The container must start a process that reads the list of
|
||||
// input Resources from stdin, reads the Configuration from the env
|
||||
// API_CONFIG, and writes the filtered Resources to stdout.
|
||||
// If there is a error or validation failure, the process must exit
|
||||
// non-zero.
|
||||
// The full set of environment variables from the parent process
|
||||
// are passed to the container.
|
||||
//
|
||||
// Function Scoping:
|
||||
// Filter applies the function only to Resources to which it is scoped.
|
||||
//
|
||||
// Resources are scoped to a function if any of the following are true:
|
||||
// - the Resource were read from the same directory as the function config
|
||||
// - the Resource were read from a subdirectory of the function config directory
|
||||
// - the function config is in a directory named "functions" and
|
||||
// they were read from a subdirectory of "functions" parent
|
||||
// - the function config doesn't have a path annotation (considered globally scoped)
|
||||
// - the Filter has GlobalScope == true
|
||||
//
|
||||
// In Scope Examples:
|
||||
//
|
||||
// Example 1: deployment.yaml and service.yaml in function.yaml scope
|
||||
// same directory as the function config directory
|
||||
// .
|
||||
// ├── function.yaml
|
||||
// ├── deployment.yaml
|
||||
// └── service.yaml
|
||||
//
|
||||
// Example 2: apps/deployment.yaml and apps/service.yaml in function.yaml scope
|
||||
// subdirectory of the function config directory
|
||||
// .
|
||||
// ├── function.yaml
|
||||
// └── apps
|
||||
// ├── deployment.yaml
|
||||
// └── service.yaml
|
||||
//
|
||||
// Example 3: apps/deployment.yaml and apps/service.yaml in functions/function.yaml scope
|
||||
// function config is in a directory named "functions"
|
||||
// .
|
||||
// ├── functions
|
||||
// │ └── function.yaml
|
||||
// └── apps
|
||||
// ├── deployment.yaml
|
||||
// └── service.yaml
|
||||
//
|
||||
// Out of Scope Examples:
|
||||
//
|
||||
// Example 1: apps/deployment.yaml and apps/service.yaml NOT in stuff/function.yaml scope
|
||||
// .
|
||||
// ├── stuff
|
||||
// │ └── function.yaml
|
||||
// └── apps
|
||||
// ├── deployment.yaml
|
||||
// └── service.yaml
|
||||
//
|
||||
// Example 2: apps/deployment.yaml and apps/service.yaml NOT in stuff/functions/function.yaml scope
|
||||
// .
|
||||
// ├── stuff
|
||||
// │ └── functions
|
||||
// │ └── function.yaml
|
||||
// └── apps
|
||||
// ├── deployment.yaml
|
||||
// └── service.yaml
|
||||
//
|
||||
// Default Paths:
|
||||
// Resources emitted by functions will have default path applied as annotations
|
||||
// if none is present.
|
||||
// The default path will be the function-dir/ (or parent directory in the case of "functions")
|
||||
// + function-file-name/ + namespace/ + kind_name.yaml
|
||||
//
|
||||
// Example 1: Given a function in fn.yaml that produces a Deployment name foo and a Service named bar
|
||||
// dir
|
||||
// └── fn.yaml
|
||||
//
|
||||
// Would default newly generated Resources to:
|
||||
//
|
||||
// dir
|
||||
// ├── fn.yaml
|
||||
// └── fn
|
||||
// ├── deployment_foo.yaml
|
||||
// └── service_bar.yaml
|
||||
//
|
||||
// Example 2: Given a function in functions/fn.yaml that produces a Deployment name foo and a Service named bar
|
||||
// dir
|
||||
// └── fn.yaml
|
||||
//
|
||||
// Would default newly generated Resources to:
|
||||
//
|
||||
// dir
|
||||
// ├── functions
|
||||
// │ └── fn.yaml
|
||||
// └── fn
|
||||
// ├── deployment_foo.yaml
|
||||
// └── service_bar.yaml
|
||||
//
|
||||
// Example 3: Given a function in fn.yaml that produces a Deployment name foo, namespace baz and a Service named bar namespace baz
|
||||
// dir
|
||||
// └── fn.yaml
|
||||
//
|
||||
// Would default newly generated Resources to:
|
||||
//
|
||||
// dir
|
||||
// ├── fn.yaml
|
||||
// └── fn
|
||||
// └── baz
|
||||
// ├── deployment_foo.yaml
|
||||
// └── service_bar.yaml
|
||||
type Filter struct {
|
||||
runtimeutil.ContainerSpec `json:",inline" yaml:",inline"`
|
||||
|
||||
Exec runtimeexec.Filter
|
||||
|
||||
UIDGID string
|
||||
}
|
||||
|
||||
func (c Filter) String() string {
|
||||
if c.Exec.DeferFailure {
|
||||
return fmt.Sprintf("%s deferFailure: %v", c.Image, c.Exec.DeferFailure)
|
||||
}
|
||||
return c.Image
|
||||
}
|
||||
func (c Filter) GetExit() error {
|
||||
return c.Exec.GetExit()
|
||||
}
|
||||
|
||||
func (c *Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
c.setupExec()
|
||||
return c.Exec.Filter(nodes)
|
||||
}
|
||||
|
||||
func (c *Filter) setupExec() {
|
||||
// don't init 2x
|
||||
if c.Exec.Path != "" {
|
||||
return
|
||||
}
|
||||
|
||||
path, args := c.getCommand()
|
||||
c.Exec.Path = path
|
||||
c.Exec.Args = args
|
||||
}
|
||||
|
||||
// getArgs returns the command + args to run to spawn the container
|
||||
func (c *Filter) getCommand() (string, []string) {
|
||||
network := runtimeutil.NetworkNameNone
|
||||
if c.ContainerSpec.Network {
|
||||
network = runtimeutil.NetworkNameHost
|
||||
}
|
||||
// run the container using docker. this is simpler than using the docker
|
||||
// libraries, and ensures things like auth work the same as if the container
|
||||
// was run from the cli.
|
||||
args := []string{"run",
|
||||
"--rm", // delete the container afterward
|
||||
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR", // attach stdin, stdout, stderr
|
||||
"--network", string(network),
|
||||
|
||||
// added security options
|
||||
"--user", c.UIDGID,
|
||||
"--security-opt=no-new-privileges", // don't allow the user to escalate privileges
|
||||
// note: don't make fs readonly because things like heredoc rely on writing tmp files
|
||||
}
|
||||
|
||||
// TODO(joncwong): Allow StorageMount fields to have default values.
|
||||
for _, storageMount := range c.StorageMounts {
|
||||
args = append(args, "--mount", storageMount.String())
|
||||
}
|
||||
|
||||
args = append(args, runtimeutil.NewContainerEnvFromStringSlice(c.Env).GetDockerFlags()...)
|
||||
a := append(args, c.Image)
|
||||
return "docker", a
|
||||
}
|
||||
|
||||
// NewContainer returns a new container filter
|
||||
func NewContainer(spec runtimeutil.ContainerSpec, uidgid string) Filter {
|
||||
f := Filter{ContainerSpec: spec, UIDGID: uidgid}
|
||||
|
||||
return f
|
||||
}
|
||||
5
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/exec/doc.go
generated
vendored
Normal file
5
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/exec/doc.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package exec contains the exec function implementation.
|
||||
package exec
|
||||
36
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/exec/exec.go
generated
vendored
Normal file
36
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/exec/exec.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type Filter struct {
|
||||
// Path is the path to the executable to run
|
||||
Path string `yaml:"path,omitempty"`
|
||||
|
||||
// Args are the arguments to the executable
|
||||
Args []string `yaml:"args,omitempty"`
|
||||
|
||||
runtimeutil.FunctionFilter
|
||||
}
|
||||
|
||||
func (c *Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
c.FunctionFilter.Run = c.Run
|
||||
return c.FunctionFilter.Filter(nodes)
|
||||
}
|
||||
|
||||
func (c *Filter) Run(reader io.Reader, writer io.Writer) error {
|
||||
cmd := exec.Command(c.Path, c.Args...)
|
||||
cmd.Stdin = reader
|
||||
cmd.Stdout = writer
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
5
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil/doc.go
generated
vendored
Normal file
5
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil/doc.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package runtimeutil contains libraries for implementing function runtimes.
|
||||
package runtimeutil
|
||||
305
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil/functiontypes.go
generated
vendored
Normal file
305
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil/functiontypes.go
generated
vendored
Normal file
@@ -0,0 +1,305 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package runtimeutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
const (
|
||||
FunctionAnnotationKey = "config.kubernetes.io/function"
|
||||
oldFunctionAnnotationKey = "config.k8s.io/function"
|
||||
)
|
||||
|
||||
var functionAnnotationKeys = []string{FunctionAnnotationKey, oldFunctionAnnotationKey}
|
||||
|
||||
// ContainerNetworkName is a type for network name used in container
|
||||
type ContainerNetworkName string
|
||||
|
||||
const (
|
||||
NetworkNameNone ContainerNetworkName = "none"
|
||||
NetworkNameHost ContainerNetworkName = "host"
|
||||
)
|
||||
const defaultEnvValue string = "true"
|
||||
|
||||
// ContainerEnv defines the environment present in a container.
|
||||
type ContainerEnv struct {
|
||||
// EnvVars is a key-value map that will be set as env in container
|
||||
EnvVars map[string]string
|
||||
|
||||
// VarsToExport are only env key. Value will be the value in the host system
|
||||
VarsToExport []string
|
||||
}
|
||||
|
||||
// GetDockerFlags returns docker run style env flags
|
||||
func (ce *ContainerEnv) GetDockerFlags() []string {
|
||||
envs := ce.EnvVars
|
||||
if envs == nil {
|
||||
envs = make(map[string]string)
|
||||
}
|
||||
|
||||
flags := []string{}
|
||||
// return in order to keep consistent among different runs
|
||||
keys := []string{}
|
||||
for k := range envs {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
flags = append(flags, "-e", key+"="+envs[key])
|
||||
}
|
||||
|
||||
for _, key := range ce.VarsToExport {
|
||||
flags = append(flags, "-e", key)
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
// AddKeyValue adds a key-value pair into the envs
|
||||
func (ce *ContainerEnv) AddKeyValue(key, value string) {
|
||||
if ce.EnvVars == nil {
|
||||
ce.EnvVars = make(map[string]string)
|
||||
}
|
||||
ce.EnvVars[key] = value
|
||||
}
|
||||
|
||||
// HasExportedKey returns true if the key is a exported key
|
||||
func (ce *ContainerEnv) HasExportedKey(key string) bool {
|
||||
for _, k := range ce.VarsToExport {
|
||||
if k == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AddKey adds a key into the envs
|
||||
func (ce *ContainerEnv) AddKey(key string) {
|
||||
if !ce.HasExportedKey(key) {
|
||||
ce.VarsToExport = append(ce.VarsToExport, key)
|
||||
}
|
||||
}
|
||||
|
||||
// Raw returns a slice of string which represents the envs.
|
||||
// Example: [foo=bar, baz]
|
||||
func (ce *ContainerEnv) Raw() []string {
|
||||
var ret []string
|
||||
for k, v := range ce.EnvVars {
|
||||
ret = append(ret, k+"="+v)
|
||||
}
|
||||
|
||||
ret = append(ret, ce.VarsToExport...)
|
||||
return ret
|
||||
}
|
||||
|
||||
// NewContainerEnv returns a pointer to a new ContainerEnv
|
||||
func NewContainerEnv() *ContainerEnv {
|
||||
var ce ContainerEnv
|
||||
ce.EnvVars = make(map[string]string)
|
||||
// default envs
|
||||
ce.EnvVars["LOG_TO_STDERR"] = defaultEnvValue
|
||||
ce.EnvVars["STRUCTURED_RESULTS"] = defaultEnvValue
|
||||
return &ce
|
||||
}
|
||||
|
||||
// NewContainerEnvFromStringSlice returns a new ContainerEnv pointer with parsing
|
||||
// input envStr. envStr example: ["foo=bar", "baz"]
|
||||
func NewContainerEnvFromStringSlice(envStr []string) *ContainerEnv {
|
||||
ce := NewContainerEnv()
|
||||
for _, e := range envStr {
|
||||
parts := strings.SplitN(e, "=", 2)
|
||||
if len(parts) == 1 {
|
||||
ce.AddKey(e)
|
||||
} else {
|
||||
ce.AddKeyValue(parts[0], parts[1])
|
||||
}
|
||||
}
|
||||
return ce
|
||||
}
|
||||
|
||||
// FunctionSpec defines a spec for running a function
|
||||
type FunctionSpec struct {
|
||||
DeferFailure bool `json:"deferFailure,omitempty" yaml:"deferFailure,omitempty"`
|
||||
|
||||
// Container is the spec for running a function as a container
|
||||
Container ContainerSpec `json:"container,omitempty" yaml:"container,omitempty"`
|
||||
|
||||
// Starlark is the spec for running a function as a starlark script
|
||||
Starlark StarlarkSpec `json:"starlark,omitempty" yaml:"starlark,omitempty"`
|
||||
|
||||
// ExecSpec is the spec for running a function as an executable
|
||||
Exec ExecSpec `json:"exec,omitempty" yaml:"exec,omitempty"`
|
||||
|
||||
// Mounts are the storage or directories to mount into the container
|
||||
StorageMounts []StorageMount `json:"mounts,omitempty" yaml:"mounts,omitempty"`
|
||||
}
|
||||
|
||||
type ExecSpec struct {
|
||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||
}
|
||||
|
||||
// ContainerSpec defines a spec for running a function as a container
|
||||
type ContainerSpec struct {
|
||||
// Image is the container image to run
|
||||
Image string `json:"image,omitempty" yaml:"image,omitempty"`
|
||||
|
||||
// Network defines network specific configuration
|
||||
Network bool `json:"network,omitempty" yaml:"network,omitempty"`
|
||||
|
||||
// Mounts are the storage or directories to mount into the container
|
||||
StorageMounts []StorageMount `json:"mounts,omitempty" yaml:"mounts,omitempty"`
|
||||
|
||||
// Env is a slice of env string that will be exposed to container
|
||||
Env []string `json:"envs,omitempty" yaml:"envs,omitempty"`
|
||||
}
|
||||
|
||||
// StarlarkSpec defines how to run a function as a starlark program
|
||||
type StarlarkSpec struct {
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
|
||||
// Path specifies a path to a starlark script
|
||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||
|
||||
// URL specifies a url containing a starlark script
|
||||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||
}
|
||||
|
||||
// StorageMount represents a container's mounted storage option(s)
|
||||
type StorageMount struct {
|
||||
// Type of mount e.g. bind mount, local volume, etc.
|
||||
MountType string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
|
||||
// Source for the storage to be mounted.
|
||||
// For named volumes, this is the name of the volume.
|
||||
// For anonymous volumes, this field is omitted (empty string).
|
||||
// For bind mounts, this is the path to the file or directory on the host.
|
||||
Src string `json:"src,omitempty" yaml:"src,omitempty"`
|
||||
|
||||
// The path where the file or directory is mounted in the container.
|
||||
DstPath string `json:"dst,omitempty" yaml:"dst,omitempty"`
|
||||
|
||||
// Mount in ReadWrite mode if it's explicitly configured
|
||||
// See https://docs.docker.com/storage/bind-mounts/#use-a-read-only-bind-mount
|
||||
ReadWriteMode bool `json:"rw,omitempty" yaml:"rw,omitempty"`
|
||||
}
|
||||
|
||||
func (s *StorageMount) String() string {
|
||||
mode := ""
|
||||
if !s.ReadWriteMode {
|
||||
mode = ",readonly"
|
||||
}
|
||||
return fmt.Sprintf("type=%s,source=%s,target=%s%s", s.MountType, s.Src, s.DstPath, mode)
|
||||
}
|
||||
|
||||
// GetFunctionSpec returns the FunctionSpec for a resource. Returns
|
||||
// nil if the resource does not have a FunctionSpec.
|
||||
//
|
||||
// The FunctionSpec is read from the resource metadata.annotation
|
||||
// "config.kubernetes.io/function"
|
||||
func GetFunctionSpec(n *yaml.RNode) *FunctionSpec {
|
||||
meta, err := n.GetMeta()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if fn := getFunctionSpecFromAnnotation(n, meta); fn != nil {
|
||||
fn.StorageMounts = []StorageMount{}
|
||||
return fn
|
||||
}
|
||||
|
||||
// legacy function specification for backwards compatibility
|
||||
container := meta.Annotations["config.kubernetes.io/container"]
|
||||
if container != "" {
|
||||
return &FunctionSpec{Container: ContainerSpec{Image: container}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getFunctionSpecFromAnnotation parses the config function from an annotation
|
||||
// if it is found
|
||||
func getFunctionSpecFromAnnotation(n *yaml.RNode, meta yaml.ResourceMeta) *FunctionSpec {
|
||||
var fs FunctionSpec
|
||||
for _, s := range functionAnnotationKeys {
|
||||
fn := meta.Annotations[s]
|
||||
if fn != "" {
|
||||
err := yaml.Unmarshal([]byte(fn), &fs)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
}
|
||||
return &fs
|
||||
}
|
||||
}
|
||||
n, err := n.Pipe(yaml.Lookup("metadata", "configFn"))
|
||||
if err != nil || yaml.IsMissingOrNull(n) {
|
||||
return nil
|
||||
}
|
||||
s, err := n.String()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
}
|
||||
err = yaml.Unmarshal([]byte(s), &fs)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
}
|
||||
return &fs
|
||||
}
|
||||
|
||||
func StringToStorageMount(s string) StorageMount {
|
||||
m := make(map[string]string)
|
||||
options := strings.Split(s, ",")
|
||||
for _, option := range options {
|
||||
keyVal := strings.SplitN(option, "=", 2)
|
||||
if len(keyVal) == 2 {
|
||||
m[keyVal[0]] = keyVal[1]
|
||||
}
|
||||
}
|
||||
var sm StorageMount
|
||||
for key, value := range m {
|
||||
switch {
|
||||
case key == "type":
|
||||
sm.MountType = value
|
||||
case key == "src" || key == "source":
|
||||
sm.Src = value
|
||||
case key == "dst" || key == "target":
|
||||
sm.DstPath = value
|
||||
case key == "rw" && value == "true":
|
||||
sm.ReadWriteMode = true
|
||||
}
|
||||
}
|
||||
return sm
|
||||
}
|
||||
|
||||
// IsReconcilerFilter filters Resources based on whether or not they are Reconciler Resource.
|
||||
// Resources with an apiVersion starting with '*.gcr.io', 'gcr.io' or 'docker.io' are considered
|
||||
// Reconciler Resources.
|
||||
type IsReconcilerFilter struct {
|
||||
// ExcludeReconcilers if set to true, then Reconcilers will be excluded -- e.g.
|
||||
// Resources with a reconcile container through the apiVersion (gcr.io prefix) or
|
||||
// through the annotations
|
||||
ExcludeReconcilers bool `yaml:"excludeReconcilers,omitempty"`
|
||||
|
||||
// IncludeNonReconcilers if set to true, the NonReconciler will be included.
|
||||
IncludeNonReconcilers bool `yaml:"includeNonReconcilers,omitempty"`
|
||||
}
|
||||
|
||||
// Filter implements kio.Filter
|
||||
func (c *IsReconcilerFilter) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
var out []*yaml.RNode
|
||||
for i := range inputs {
|
||||
isFnResource := GetFunctionSpec(inputs[i]) != nil
|
||||
if isFnResource && !c.ExcludeReconcilers {
|
||||
out = append(out, inputs[i])
|
||||
}
|
||||
if !isFnResource && c.IncludeNonReconcilers {
|
||||
out = append(out, inputs[i])
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
256
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil/runtimeutil.go
generated
vendored
Normal file
256
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil/runtimeutil.go
generated
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package runtimeutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/comments"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// FunctionFilter wraps another filter to be invoked in the context of a function.
|
||||
// FunctionFilter manages scoping the function, deferring failures, and saving results
|
||||
// to files.
|
||||
type FunctionFilter struct {
|
||||
// Run implements the function.
|
||||
Run func(reader io.Reader, writer io.Writer) error
|
||||
|
||||
// FunctionConfig is passed to the function through ResourceList.functionConfig.
|
||||
FunctionConfig *yaml.RNode `yaml:"functionConfig,omitempty"`
|
||||
|
||||
// GlobalScope explicitly scopes the function to all input resources rather than only those
|
||||
// resources scoped to it by path.
|
||||
GlobalScope bool
|
||||
|
||||
// ResultsFile is the file to write function ResourceList.results to.
|
||||
// If unset, results will not be written.
|
||||
ResultsFile string
|
||||
|
||||
// DeferFailure will cause the Filter to return a nil error even if Run returns an error.
|
||||
// The Run error will be available through GetExit().
|
||||
DeferFailure bool
|
||||
|
||||
// results saves the results emitted from Run
|
||||
results *yaml.RNode
|
||||
|
||||
// exit saves the error returned from Run
|
||||
exit error
|
||||
|
||||
ids map[string]*yaml.RNode
|
||||
}
|
||||
|
||||
// GetExit returns the error from Run
|
||||
func (c FunctionFilter) GetExit() error {
|
||||
return c.exit
|
||||
}
|
||||
|
||||
// functionsDirectoryName is keyword directory name for functions scoped 1 directory higher
|
||||
const functionsDirectoryName = "functions"
|
||||
|
||||
// getFunctionScope returns the path of the directory containing the function config,
|
||||
// or its parent directory if the base directory is named "functions"
|
||||
func (c *FunctionFilter) getFunctionScope() (string, error) {
|
||||
m, err := c.FunctionConfig.GetMeta()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err)
|
||||
}
|
||||
p, found := m.Annotations[kioutil.PathAnnotation]
|
||||
if !found {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
functionDir := path.Clean(path.Dir(p))
|
||||
|
||||
if path.Base(functionDir) == functionsDirectoryName {
|
||||
// the scope of functions in a directory called "functions" is 1 level higher
|
||||
// this is similar to how the golang "internal" directory scoping works
|
||||
functionDir = path.Dir(functionDir)
|
||||
}
|
||||
return functionDir, nil
|
||||
}
|
||||
|
||||
// scope partitions the input nodes into 2 slices. The first slice contains only Resources
|
||||
// which are scoped under dir, and the second slice contains the Resources which are not.
|
||||
func (c *FunctionFilter) scope(dir string, nodes []*yaml.RNode) ([]*yaml.RNode, []*yaml.RNode, error) {
|
||||
// scope container filtered Resources to Resources under that directory
|
||||
var input, saved []*yaml.RNode
|
||||
if c.GlobalScope {
|
||||
return nodes, nil, nil
|
||||
}
|
||||
|
||||
// global function
|
||||
if dir == "" || dir == "." {
|
||||
return nodes, nil, nil
|
||||
}
|
||||
|
||||
// identify Resources read from directories under the function configuration
|
||||
for i := range nodes {
|
||||
m, err := nodes[i].GetMeta()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
p, found := m.Annotations[kioutil.PathAnnotation]
|
||||
if !found {
|
||||
// this Resource isn't scoped under the function -- don't know where it came from
|
||||
// consider it out of scope
|
||||
saved = append(saved, nodes[i])
|
||||
continue
|
||||
}
|
||||
|
||||
resourceDir := path.Clean(path.Dir(p))
|
||||
if path.Base(resourceDir) == functionsDirectoryName {
|
||||
// Functions in the `functions` directory are scoped to
|
||||
// themselves, and should see themselves as input
|
||||
resourceDir = path.Dir(resourceDir)
|
||||
}
|
||||
if !strings.HasPrefix(resourceDir, dir) {
|
||||
// this Resource doesn't fall under the function scope if it
|
||||
// isn't in a subdirectory of where the function lives
|
||||
saved = append(saved, nodes[i])
|
||||
continue
|
||||
}
|
||||
|
||||
// this input is scoped under the function
|
||||
input = append(input, nodes[i])
|
||||
}
|
||||
|
||||
return input, saved, nil
|
||||
}
|
||||
|
||||
func (c *FunctionFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
in := &bytes.Buffer{}
|
||||
out := &bytes.Buffer{}
|
||||
|
||||
// only process Resources scoped to this function, save the others
|
||||
functionDir, err := c.getFunctionScope()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
input, saved, err := c.scope(functionDir, nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// set ids on each input so it is possible to copy comments from inputs back to outputs
|
||||
if err := c.setIds(input); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// write the input
|
||||
err = kio.ByteWriter{
|
||||
WrappingAPIVersion: kio.ResourceListAPIVersion,
|
||||
WrappingKind: kio.ResourceListKind,
|
||||
Writer: in,
|
||||
KeepReaderAnnotations: true,
|
||||
FunctionConfig: c.FunctionConfig}.Write(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// capture the command stdout for the return value
|
||||
r := &kio.ByteReader{Reader: out}
|
||||
|
||||
// don't exit immediately if the function fails -- write out the validation
|
||||
c.exit = c.Run(in, out)
|
||||
|
||||
output, err := r.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// copy the comments from the inputs to the outputs
|
||||
if err := c.setComments(output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := c.doResults(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.exit != nil && !c.DeferFailure {
|
||||
return append(output, saved...), c.exit
|
||||
}
|
||||
|
||||
// annotate any generated Resources with a path and index if they don't already have one
|
||||
if err := kioutil.DefaultPathAnnotation(functionDir, output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// emit both the Resources output from the function, and the out-of-scope Resources
|
||||
// which were not provided to the function
|
||||
return append(output, saved...), nil
|
||||
}
|
||||
|
||||
const idAnnotation = "config.k8s.io/id"
|
||||
|
||||
func (c *FunctionFilter) setIds(nodes []*yaml.RNode) error {
|
||||
// set the id on each node to map inputs to outputs
|
||||
var id int
|
||||
c.ids = map[string]*yaml.RNode{}
|
||||
for i := range nodes {
|
||||
id++
|
||||
idStr := fmt.Sprintf("%v", id)
|
||||
err := nodes[i].PipeE(yaml.SetAnnotation(idAnnotation, idStr))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
c.ids[idStr] = nodes[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *FunctionFilter) setComments(nodes []*yaml.RNode) error {
|
||||
for i := range nodes {
|
||||
node := nodes[i]
|
||||
anID, err := node.Pipe(yaml.GetAnnotation(idAnnotation))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if anID == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var in *yaml.RNode
|
||||
var found bool
|
||||
if in, found = c.ids[anID.YNode().Value]; !found {
|
||||
continue
|
||||
}
|
||||
if err := comments.CopyComments(in, node); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if err := node.PipeE(yaml.ClearAnnotation(idAnnotation)); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *FunctionFilter) doResults(r *kio.ByteReader) error {
|
||||
// Write the results to a file if configured to do so
|
||||
if c.ResultsFile != "" && r.Results != nil {
|
||||
results, err := r.Results.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(c.ResultsFile, []byte(results), 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if r.Results != nil {
|
||||
c.results = r.Results
|
||||
}
|
||||
return nil
|
||||
}
|
||||
8
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil/types.go
generated
vendored
Normal file
8
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil/types.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package runtimeutil
|
||||
|
||||
type DeferFailureFunction interface {
|
||||
GetExit() error
|
||||
}
|
||||
79
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark/context.go
generated
vendored
Normal file
79
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark/context.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package starlark
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"go.starlark.net/starlark"
|
||||
"go.starlark.net/starlarkstruct"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/qri-io/starlib/util"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
resourceList starlark.Value
|
||||
}
|
||||
|
||||
func (c *Context) predeclared() (starlark.StringDict, error) {
|
||||
e, err := env()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oa, err := oa()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dict := starlark.StringDict{
|
||||
"resource_list": c.resourceList,
|
||||
"open_api": oa,
|
||||
"environment": e,
|
||||
}
|
||||
|
||||
return starlark.StringDict{
|
||||
"ctx": starlarkstruct.FromStringDict(starlarkstruct.Default, dict),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func oa() (starlark.Value, error) {
|
||||
return interfaceToValue(openapi.Schema())
|
||||
}
|
||||
|
||||
func env() (starlark.Value, error) {
|
||||
env := map[string]interface{}{}
|
||||
for _, e := range os.Environ() {
|
||||
pair := strings.SplitN(e, "=", 2)
|
||||
if len(pair) < 2 {
|
||||
continue
|
||||
}
|
||||
env[pair[0]] = pair[1]
|
||||
}
|
||||
value, err := util.Marshal(env)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func interfaceToValue(i interface{}) (starlark.Value, error) {
|
||||
b, err := json.Marshal(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var in map[string]interface{}
|
||||
if err := yaml.Unmarshal(b, &in); err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
value, err := util.Marshal(in)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
36
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark/doc.go
generated
vendored
Normal file
36
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark/doc.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package starlark contains a kio.Filter which can be applied to resources to transform
|
||||
// them through starlark program.
|
||||
//
|
||||
// Starlark has become a popular runtime embedding in go programs, especially for Kubernetes
|
||||
// and data processing.
|
||||
// Examples: https://github.com/cruise-automation/isopod, https://qri.io/docs/starlark/starlib,
|
||||
// https://github.com/stripe/skycfg, https://github.com/k14s/ytt
|
||||
//
|
||||
// The resources are provided to the starlark program through the global variable "resourceList".
|
||||
// "resourceList" is a dictionary containing an "items" field with a list of resources.
|
||||
// The starlark modified "resourceList" is the Filter output.
|
||||
//
|
||||
// After being run through the starlark program, the filter will copy the comments from the input
|
||||
// resources to restore them -- due to them being dropped as a result of serializing the resources
|
||||
// as starlark values.
|
||||
//
|
||||
// "resourceList" may also contain a "functionConfig" entry to configure the starlark script itself.
|
||||
// Changes made by the starlark program to the "functionConfig" will be reflected in the
|
||||
// Filter.FunctionConfig value.
|
||||
//
|
||||
// The Filter will also format the output so that output has the preferred field ordering
|
||||
// rather than an alphabetical field ordering.
|
||||
//
|
||||
// The resourceList variable adheres to the kustomize function spec as specified by:
|
||||
// https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md
|
||||
//
|
||||
// All items in the resourceList are resources represented as starlark dictionaries/
|
||||
// The items in the resourceList respect the io spec specified by:
|
||||
// https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/config-io.md
|
||||
//
|
||||
// The starlark language spec can be found here:
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md
|
||||
package starlark
|
||||
181
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark/starlark.go
generated
vendored
Normal file
181
vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark/starlark.go
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package starlark
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"go.starlark.net/starlark"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/qri-io/starlib/util"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Filter transforms a set of resources through the provided program
|
||||
type Filter struct {
|
||||
Name string
|
||||
|
||||
// Program is a starlark script which will be run against the resources
|
||||
Program string
|
||||
|
||||
// URL is the url of a starlark program to fetch and run
|
||||
URL string
|
||||
|
||||
// Path is the path to a starlark program to read and run
|
||||
Path string
|
||||
|
||||
runtimeutil.FunctionFilter
|
||||
}
|
||||
|
||||
func (sf *Filter) String() string {
|
||||
return fmt.Sprintf(
|
||||
"name: %v path: %v url: %v program: %v", sf.Name, sf.Path, sf.URL, sf.Program)
|
||||
}
|
||||
|
||||
func (sf *Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
err := sf.setup()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sf.FunctionFilter.Run = sf.Run
|
||||
|
||||
return sf.FunctionFilter.Filter(nodes)
|
||||
}
|
||||
|
||||
func (sf *Filter) setup() error {
|
||||
if (sf.URL != "" && sf.Path != "") ||
|
||||
(sf.URL != "" && sf.Program != "") ||
|
||||
(sf.Path != "" && sf.Program != "") {
|
||||
return errors.Errorf("Filter Path, Program and URL are mutually exclusive")
|
||||
}
|
||||
|
||||
// read the program from a file
|
||||
if sf.Path != "" {
|
||||
b, err := ioutil.ReadFile(sf.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sf.Program = string(b)
|
||||
}
|
||||
|
||||
// read the program from a URL
|
||||
if sf.URL != "" {
|
||||
err := func() error {
|
||||
resp, err := http.Get(sf.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sf.Program = string(b)
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sf *Filter) Run(reader io.Reader, writer io.Writer) error {
|
||||
// retain map of inputs to outputs by id so if the name is changed by the
|
||||
// starlark program, we are able to match the same resources
|
||||
value, err := sf.readResourceList(reader)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// run the starlark as program as transformation function
|
||||
thread := &starlark.Thread{Name: sf.Name}
|
||||
|
||||
ctx := &Context{resourceList: value}
|
||||
pd, err := ctx.predeclared()
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
_, err = starlark.ExecFile(thread, sf.Name, sf.Program, pd)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
return sf.writeResourceList(value, writer)
|
||||
}
|
||||
|
||||
// inputToResourceList transforms input into a starlark.Value
|
||||
func (sf *Filter) readResourceList(reader io.Reader) (starlark.Value, error) {
|
||||
// read and parse the inputs
|
||||
rl := bytes.Buffer{}
|
||||
_, err := rl.ReadFrom(reader)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
rn, err := yaml.Parse(rl.String())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
// convert to a starlark value
|
||||
b, err := yaml.Marshal(rn.Document()) // convert to bytes
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
var in map[string]interface{}
|
||||
err = yaml.Unmarshal(b, &in) // convert to map[string]interface{}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
return util.Marshal(in) // convert to starlark value
|
||||
}
|
||||
|
||||
// resourceListToOutput converts the output of the starlark program to the filter output
|
||||
func (sf *Filter) writeResourceList(value starlark.Value, writer io.Writer) error {
|
||||
// convert the modified resourceList back into a slice of RNodes
|
||||
// by first converting to a map[string]interface{}
|
||||
out, err := util.Unmarshal(value)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
b, err := yaml.Marshal(out)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
rl, err := yaml.Parse(string(b))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// preserve the comments from the input
|
||||
items, err := rl.Pipe(yaml.Lookup("items"))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
err = items.VisitElements(func(node *yaml.RNode) error {
|
||||
// starlark will serialize the resources sorting the fields alphabetically,
|
||||
// format them to have a better ordering
|
||||
_, err := filters.FormatFilter{}.Filter([]*yaml.RNode{node})
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
s, err := rl.String()
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
_, err = writer.Write([]byte(s))
|
||||
return err
|
||||
}
|
||||
21
vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/qri-io/starlib/util/LICENSE
generated
vendored
Normal file
21
vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/qri-io/starlib/util/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 QRI, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
25
vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/qri-io/starlib/util/doc.go
generated
vendored
Normal file
25
vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/qri-io/starlib/util/doc.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// The MIT License (MIT)
|
||||
|
||||
// Copyright (c) 2018 QRI, Inc.
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
// Package util is forked from https://github.com/qri-io/starlib in order to prune
|
||||
// excessive transitive dependencies from pulling in that library.
|
||||
package util
|
||||
273
vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/qri-io/starlib/util/util.go
generated
vendored
Normal file
273
vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/qri-io/starlib/util/util.go
generated
vendored
Normal file
@@ -0,0 +1,273 @@
|
||||
// The MIT License (MIT)
|
||||
|
||||
// Copyright (c) 2018 QRI, Inc.
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.starlark.net/starlark"
|
||||
"go.starlark.net/starlarkstruct"
|
||||
)
|
||||
|
||||
// // asString unquotes a starlark string value
|
||||
// func asString(x starlark.Value) (string, error) {
|
||||
// return strconv.Unquote(x.String())
|
||||
// }
|
||||
|
||||
// IsEmptyString checks is a starlark string is empty ("" for a go string)
|
||||
// starlark.String.String performs repr-style quotation, which is necessary
|
||||
// for the starlark.Value contract but a frequent source of errors in API
|
||||
// clients. This helper method makes sure it'll work properly
|
||||
func IsEmptyString(s starlark.String) bool {
|
||||
return s.String() == `""`
|
||||
}
|
||||
|
||||
// Unmarshal decodes a starlark.Value into it's golang counterpart
|
||||
//nolint:nakedret
|
||||
func Unmarshal(x starlark.Value) (val interface{}, err error) {
|
||||
switch v := x.(type) {
|
||||
case starlark.NoneType:
|
||||
val = nil
|
||||
case starlark.Bool:
|
||||
val = v.Truth() == starlark.True
|
||||
case starlark.Int:
|
||||
val, err = starlark.AsInt32(x)
|
||||
case starlark.Float:
|
||||
if f, ok := starlark.AsFloat(x); !ok {
|
||||
err = fmt.Errorf("couldn't parse float")
|
||||
} else {
|
||||
val = f
|
||||
}
|
||||
case starlark.String:
|
||||
val = v.GoString()
|
||||
// case starlibtime.Time:
|
||||
// val = time.Time(v)
|
||||
case *starlark.Dict:
|
||||
var (
|
||||
dictVal starlark.Value
|
||||
pval interface{}
|
||||
kval interface{}
|
||||
keys []interface{}
|
||||
vals []interface{}
|
||||
// key as interface if found one key is not a string
|
||||
ki bool
|
||||
)
|
||||
|
||||
for _, k := range v.Keys() {
|
||||
dictVal, _, err = v.Get(k)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pval, err = Unmarshal(dictVal)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unmarshaling starlark value: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
kval, err = Unmarshal(k)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unmarshaling starlark key: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := kval.(string); !ok {
|
||||
// found key as not a string
|
||||
ki = true
|
||||
}
|
||||
|
||||
keys = append(keys, kval)
|
||||
vals = append(vals, pval)
|
||||
}
|
||||
|
||||
// prepare result
|
||||
|
||||
rs := map[string]interface{}{}
|
||||
ri := map[interface{}]interface{}{}
|
||||
|
||||
for i, key := range keys {
|
||||
// key as interface
|
||||
if ki {
|
||||
ri[key] = vals[i]
|
||||
} else {
|
||||
rs[key.(string)] = vals[i]
|
||||
}
|
||||
}
|
||||
|
||||
if ki {
|
||||
val = ri // map[interface{}]interface{}
|
||||
} else {
|
||||
val = rs // map[string]interface{}
|
||||
}
|
||||
case *starlark.List:
|
||||
var (
|
||||
i int
|
||||
listVal starlark.Value
|
||||
iter = v.Iterate()
|
||||
value = make([]interface{}, v.Len())
|
||||
)
|
||||
|
||||
defer iter.Done()
|
||||
for iter.Next(&listVal) {
|
||||
value[i], err = Unmarshal(listVal)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
i++
|
||||
}
|
||||
val = value
|
||||
case starlark.Tuple:
|
||||
var (
|
||||
i int
|
||||
tupleVal starlark.Value
|
||||
iter = v.Iterate()
|
||||
value = make([]interface{}, v.Len())
|
||||
)
|
||||
|
||||
defer iter.Done()
|
||||
for iter.Next(&tupleVal) {
|
||||
value[i], err = Unmarshal(tupleVal)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
i++
|
||||
}
|
||||
val = value
|
||||
case *starlark.Set:
|
||||
fmt.Println("errnotdone: SET")
|
||||
err = fmt.Errorf("sets aren't yet supported")
|
||||
case *starlarkstruct.Struct:
|
||||
if _var, ok := v.Constructor().(Unmarshaler); ok {
|
||||
err = _var.UnmarshalStarlark(x)
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "failed marshal %q to Starlark object", v.Constructor().Type())
|
||||
return
|
||||
}
|
||||
val = _var
|
||||
} else {
|
||||
err = fmt.Errorf("constructor object from *starlarkstruct.Struct not supported Marshaler to starlark object: %s", v.Constructor().Type())
|
||||
}
|
||||
default:
|
||||
fmt.Println("errbadtype:", x.Type())
|
||||
err = fmt.Errorf("unrecognized starlark type: %s", x.Type())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Marshal turns go values into starlark types
|
||||
//nolint:nakedret
|
||||
func Marshal(data interface{}) (v starlark.Value, err error) {
|
||||
switch x := data.(type) {
|
||||
case nil:
|
||||
v = starlark.None
|
||||
case bool:
|
||||
v = starlark.Bool(x)
|
||||
case string:
|
||||
v = starlark.String(x)
|
||||
case int:
|
||||
v = starlark.MakeInt(x)
|
||||
case int8:
|
||||
v = starlark.MakeInt(int(x))
|
||||
case int16:
|
||||
v = starlark.MakeInt(int(x))
|
||||
case int32:
|
||||
v = starlark.MakeInt(int(x))
|
||||
case int64:
|
||||
v = starlark.MakeInt64(x)
|
||||
case uint:
|
||||
v = starlark.MakeUint(x)
|
||||
case uint8:
|
||||
v = starlark.MakeUint(uint(x))
|
||||
case uint16:
|
||||
v = starlark.MakeUint(uint(x))
|
||||
case uint32:
|
||||
v = starlark.MakeUint(uint(x))
|
||||
case uint64:
|
||||
v = starlark.MakeUint64(x)
|
||||
case float32:
|
||||
v = starlark.Float(float64(x))
|
||||
case float64:
|
||||
v = starlark.Float(x)
|
||||
// case time.Time:
|
||||
// v = starlibtime.Time(x)
|
||||
case []interface{}:
|
||||
var elems = make([]starlark.Value, len(x))
|
||||
for i, val := range x {
|
||||
elems[i], err = Marshal(val)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
v = starlark.NewList(elems)
|
||||
case map[interface{}]interface{}:
|
||||
dict := &starlark.Dict{}
|
||||
var elem starlark.Value
|
||||
for ki, val := range x {
|
||||
var key starlark.Value
|
||||
key, err = Marshal(ki)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
elem, err = Marshal(val)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = dict.SetKey(key, elem); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
v = dict
|
||||
case map[string]interface{}:
|
||||
dict := &starlark.Dict{}
|
||||
var elem starlark.Value
|
||||
for key, val := range x {
|
||||
elem, err = Marshal(val)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = dict.SetKey(starlark.String(key), elem); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
v = dict
|
||||
case Marshaler:
|
||||
v, err = x.MarshalStarlark()
|
||||
default:
|
||||
return starlark.None, fmt.Errorf("unrecognized type: %#v", x)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Unmarshaler is the interface use to unmarshal starlark custom types.
|
||||
type Unmarshaler interface {
|
||||
// UnmarshalStarlark unmarshal a starlark object to custom type.
|
||||
UnmarshalStarlark(starlark.Value) error
|
||||
}
|
||||
|
||||
// Marshaler is the interface use to marshal starlark custom types.
|
||||
type Marshaler interface {
|
||||
// MarshalStarlark marshal a custom type to starlark object.
|
||||
MarshalStarlark() (starlark.Value, error)
|
||||
}
|
||||
248
vendor/sigs.k8s.io/kustomize/kyaml/kio/byteio_reader.go
generated
vendored
Normal file
248
vendor/sigs.k8s.io/kustomize/kyaml/kio/byteio_reader.go
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
const (
|
||||
ResourceListKind = "ResourceList"
|
||||
ResourceListAPIVersion = "config.kubernetes.io/v1alpha1"
|
||||
)
|
||||
|
||||
// ByteReadWriter reads from an input and writes to an output.
|
||||
type ByteReadWriter struct {
|
||||
// Reader is where ResourceNodes are decoded from.
|
||||
Reader io.Reader
|
||||
|
||||
// Writer is where ResourceNodes are encoded.
|
||||
Writer io.Writer
|
||||
|
||||
// OmitReaderAnnotations will configures Read to skip setting the config.kubernetes.io/index
|
||||
// annotation on Resources as they are Read.
|
||||
OmitReaderAnnotations bool
|
||||
|
||||
// KeepReaderAnnotations if set will keep the Reader specific annotations when writing
|
||||
// the Resources, otherwise they will be cleared.
|
||||
KeepReaderAnnotations bool
|
||||
|
||||
// Style is a style that is set on the Resource Node Document.
|
||||
Style yaml.Style
|
||||
|
||||
FunctionConfig *yaml.RNode
|
||||
|
||||
Results *yaml.RNode
|
||||
|
||||
NoWrap bool
|
||||
WrappingAPIVersion string
|
||||
WrappingKind string
|
||||
}
|
||||
|
||||
func (rw *ByteReadWriter) Read() ([]*yaml.RNode, error) {
|
||||
b := &ByteReader{
|
||||
Reader: rw.Reader,
|
||||
OmitReaderAnnotations: rw.OmitReaderAnnotations,
|
||||
}
|
||||
val, err := b.Read()
|
||||
if rw.FunctionConfig == nil {
|
||||
rw.FunctionConfig = b.FunctionConfig
|
||||
}
|
||||
rw.Results = b.Results
|
||||
|
||||
if !rw.NoWrap {
|
||||
rw.WrappingAPIVersion = b.WrappingAPIVersion
|
||||
rw.WrappingKind = b.WrappingKind
|
||||
}
|
||||
return val, errors.Wrap(err)
|
||||
}
|
||||
|
||||
func (rw *ByteReadWriter) Write(nodes []*yaml.RNode) error {
|
||||
return ByteWriter{
|
||||
Writer: rw.Writer,
|
||||
KeepReaderAnnotations: rw.KeepReaderAnnotations,
|
||||
Style: rw.Style,
|
||||
FunctionConfig: rw.FunctionConfig,
|
||||
Results: rw.Results,
|
||||
WrappingAPIVersion: rw.WrappingAPIVersion,
|
||||
WrappingKind: rw.WrappingKind,
|
||||
}.Write(nodes)
|
||||
}
|
||||
|
||||
// ParseAll reads all of the inputs into resources
|
||||
func ParseAll(inputs ...string) ([]*yaml.RNode, error) {
|
||||
return (&ByteReader{
|
||||
Reader: bytes.NewBufferString(strings.Join(inputs, "\n---\n")),
|
||||
}).Read()
|
||||
}
|
||||
|
||||
// FromBytes reads from a byte slice.
|
||||
func FromBytes(bs []byte) ([]*yaml.RNode, error) {
|
||||
return (&ByteReader{
|
||||
OmitReaderAnnotations: true,
|
||||
Reader: bytes.NewBuffer(bs),
|
||||
}).Read()
|
||||
}
|
||||
|
||||
// StringAll writes all of the resources to a string
|
||||
func StringAll(resources []*yaml.RNode) (string, error) {
|
||||
var b bytes.Buffer
|
||||
err := (&ByteWriter{Writer: &b}).Write(resources)
|
||||
return b.String(), err
|
||||
}
|
||||
|
||||
// ByteReader decodes ResourceNodes from bytes.
|
||||
// By default, Read will set the config.kubernetes.io/index annotation on each RNode as it
|
||||
// is read so they can be written back in the same order.
|
||||
type ByteReader struct {
|
||||
// Reader is where ResourceNodes are decoded from.
|
||||
Reader io.Reader
|
||||
|
||||
// OmitReaderAnnotations will configures Read to skip setting the config.kubernetes.io/index
|
||||
// annotation on Resources as they are Read.
|
||||
OmitReaderAnnotations bool
|
||||
|
||||
// SetAnnotations is a map of caller specified annotations to set on resources as they are read
|
||||
// These are independent of the annotations controlled by OmitReaderAnnotations
|
||||
SetAnnotations map[string]string
|
||||
|
||||
FunctionConfig *yaml.RNode
|
||||
|
||||
Results *yaml.RNode
|
||||
|
||||
// DisableUnwrapping prevents Resources in Lists and ResourceLists from being unwrapped
|
||||
DisableUnwrapping bool
|
||||
|
||||
// WrappingAPIVersion is set by Read(), and is the apiVersion of the object that
|
||||
// the read objects were originally wrapped in.
|
||||
WrappingAPIVersion string
|
||||
|
||||
// WrappingKind is set by Read(), and is the kind of the object that
|
||||
// the read objects were originally wrapped in.
|
||||
WrappingKind string
|
||||
}
|
||||
|
||||
var _ Reader = &ByteReader{}
|
||||
|
||||
func (r *ByteReader) Read() ([]*yaml.RNode, error) {
|
||||
output := ResourceNodeSlice{}
|
||||
|
||||
// by manually splitting resources -- otherwise the decoder will get the Resource
|
||||
// boundaries wrong for header comments.
|
||||
input := &bytes.Buffer{}
|
||||
_, err := io.Copy(input, r.Reader)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
// replace the ending \r\n (line ending used in windows) with \n and then separate by \n---\n
|
||||
values := strings.Split(strings.ReplaceAll(input.String(), "\r\n", "\n"), "\n---\n")
|
||||
|
||||
index := 0
|
||||
for i := range values {
|
||||
// the Split used above will eat the tail '\n' from each resource. This may affect the
|
||||
// literal string value since '\n' is meaningful in it.
|
||||
if i != len(values)-1 {
|
||||
values[i] += "\n"
|
||||
}
|
||||
decoder := yaml.NewDecoder(bytes.NewBufferString(values[i]))
|
||||
node, err := r.decode(index, decoder)
|
||||
if err == io.EOF {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
if yaml.IsMissingOrNull(node) {
|
||||
// empty value
|
||||
continue
|
||||
}
|
||||
|
||||
// ok if no metadata -- assume not an InputList
|
||||
meta, err := node.GetMeta()
|
||||
if err != yaml.ErrMissingMetadata && err != nil {
|
||||
return nil, errors.WrapPrefixf(err, "[%d]", i)
|
||||
}
|
||||
|
||||
// the elements are wrapped in an InputList, unwrap them
|
||||
// don't check apiVersion, we haven't standardized on the domain
|
||||
if !r.DisableUnwrapping &&
|
||||
len(values) == 1 && // Only unwrap if there is only 1 value
|
||||
(meta.Kind == ResourceListKind || meta.Kind == "List") &&
|
||||
(node.Field("items") != nil || node.Field("functionConfig") != nil) {
|
||||
r.WrappingKind = meta.Kind
|
||||
r.WrappingAPIVersion = meta.APIVersion
|
||||
|
||||
// unwrap the list
|
||||
if fc := node.Field("functionConfig"); fc != nil {
|
||||
r.FunctionConfig = fc.Value
|
||||
}
|
||||
if res := node.Field("results"); res != nil {
|
||||
r.Results = res.Value
|
||||
}
|
||||
|
||||
items := node.Field("items")
|
||||
if items != nil {
|
||||
for i := range items.Value.Content() {
|
||||
// add items
|
||||
output = append(output, yaml.NewRNode(items.Value.Content()[i]))
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// add the node to the list
|
||||
output = append(output, node)
|
||||
|
||||
// increment the index annotation value
|
||||
index++
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func (r *ByteReader) decode(index int, decoder *yaml.Decoder) (*yaml.RNode, error) {
|
||||
node := &yaml.Node{}
|
||||
err := decoder.Decode(node)
|
||||
if err == io.EOF {
|
||||
return nil, io.EOF
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
if yaml.IsYNodeEmptyDoc(node) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// set annotations on the read Resources
|
||||
// sort the annotations by key so the output Resources is consistent (otherwise the
|
||||
// annotations will be in a random order)
|
||||
n := yaml.NewRNode(node)
|
||||
if r.SetAnnotations == nil {
|
||||
r.SetAnnotations = map[string]string{}
|
||||
}
|
||||
if !r.OmitReaderAnnotations {
|
||||
r.SetAnnotations[kioutil.IndexAnnotation] = fmt.Sprintf("%d", index)
|
||||
}
|
||||
var keys []string
|
||||
for k := range r.SetAnnotations {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
_, err = n.Pipe(yaml.SetAnnotation(k, r.SetAnnotations[k]))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
return yaml.NewRNode(node), nil
|
||||
}
|
||||
140
vendor/sigs.k8s.io/kustomize/kyaml/kio/byteio_writer.go
generated
vendored
Normal file
140
vendor/sigs.k8s.io/kustomize/kyaml/kio/byteio_writer.go
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kio
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Writer writes ResourceNodes to bytes.
|
||||
type ByteWriter struct {
|
||||
// Writer is where ResourceNodes are encoded.
|
||||
Writer io.Writer
|
||||
|
||||
// KeepReaderAnnotations if set will keep the Reader specific annotations when writing
|
||||
// the Resources, otherwise they will be cleared.
|
||||
KeepReaderAnnotations bool
|
||||
|
||||
// ClearAnnotations is a list of annotations to clear when writing the Resources.
|
||||
ClearAnnotations []string
|
||||
|
||||
// Style is a style that is set on the Resource Node Document.
|
||||
Style yaml.Style
|
||||
|
||||
// FunctionConfig is the function config for an ResourceList. If non-nil
|
||||
// wrap the results in an ResourceList.
|
||||
FunctionConfig *yaml.RNode
|
||||
|
||||
Results *yaml.RNode
|
||||
|
||||
// WrappingKind if set will cause ByteWriter to wrap the Resources in
|
||||
// an 'items' field in this kind. e.g. if WrappingKind is 'List',
|
||||
// ByteWriter will wrap the Resources in a List .items field.
|
||||
WrappingKind string
|
||||
|
||||
// WrappingAPIVersion is the apiVersion for WrappingKind
|
||||
WrappingAPIVersion string
|
||||
|
||||
// Sort if set, will cause ByteWriter to sort the the nodes before writing them.
|
||||
Sort bool
|
||||
}
|
||||
|
||||
var _ Writer = ByteWriter{}
|
||||
|
||||
func (w ByteWriter) Write(nodes []*yaml.RNode) error {
|
||||
yaml.DoSerializationHacksOnNodes(nodes)
|
||||
if w.Sort {
|
||||
if err := kioutil.SortNodes(nodes); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
encoder := yaml.NewEncoder(w.Writer)
|
||||
defer encoder.Close()
|
||||
for i := range nodes {
|
||||
// clean resources by removing annotations set by the Reader
|
||||
if !w.KeepReaderAnnotations {
|
||||
_, err := nodes[i].Pipe(yaml.ClearAnnotation(kioutil.IndexAnnotation))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
for _, a := range w.ClearAnnotations {
|
||||
_, err := nodes[i].Pipe(yaml.ClearAnnotation(a))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := yaml.ClearEmptyAnnotations(nodes[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if w.Style != 0 {
|
||||
nodes[i].YNode().Style = w.Style
|
||||
}
|
||||
}
|
||||
|
||||
// don't wrap the elements
|
||||
if w.WrappingKind == "" {
|
||||
for i := range nodes {
|
||||
if err := w.encode(encoder, nodes[i].Document()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// wrap the elements in a list
|
||||
items := &yaml.Node{Kind: yaml.SequenceNode}
|
||||
list := &yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
Style: w.Style,
|
||||
Content: []*yaml.Node{
|
||||
{Kind: yaml.ScalarNode, Value: "apiVersion"},
|
||||
{Kind: yaml.ScalarNode, Value: w.WrappingAPIVersion},
|
||||
{Kind: yaml.ScalarNode, Value: "kind"},
|
||||
{Kind: yaml.ScalarNode, Value: w.WrappingKind},
|
||||
{Kind: yaml.ScalarNode, Value: "items"}, items,
|
||||
}}
|
||||
if w.FunctionConfig != nil {
|
||||
list.Content = append(list.Content,
|
||||
&yaml.Node{Kind: yaml.ScalarNode, Value: "functionConfig"},
|
||||
w.FunctionConfig.YNode())
|
||||
}
|
||||
if w.Results != nil {
|
||||
list.Content = append(list.Content,
|
||||
&yaml.Node{Kind: yaml.ScalarNode, Value: "results"},
|
||||
w.Results.YNode())
|
||||
}
|
||||
doc := &yaml.Node{
|
||||
Kind: yaml.DocumentNode,
|
||||
Content: []*yaml.Node{list}}
|
||||
for i := range nodes {
|
||||
items.Content = append(items.Content, nodes[i].YNode())
|
||||
}
|
||||
err := w.encode(encoder, doc)
|
||||
yaml.UndoSerializationHacksOnNodes(nodes)
|
||||
return err
|
||||
}
|
||||
|
||||
// encode encodes the input document node to appropriate node format
|
||||
func (w ByteWriter) encode(encoder *yaml.Encoder, doc *yaml.Node) error {
|
||||
rNode := &yaml.RNode{}
|
||||
rNode.SetYNode(doc)
|
||||
str, err := rNode.String()
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if json.Valid([]byte(str)) {
|
||||
je := json.NewEncoder(w.Writer)
|
||||
je.SetIndent("", " ")
|
||||
return errors.Wrap(je.Encode(rNode))
|
||||
}
|
||||
return encoder.Encode(doc)
|
||||
}
|
||||
35
vendor/sigs.k8s.io/kustomize/kyaml/kio/doc.go
generated
vendored
Normal file
35
vendor/sigs.k8s.io/kustomize/kyaml/kio/doc.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package kio contains libraries for reading and writing collections of Resources.
|
||||
//
|
||||
// Reading Resources
|
||||
//
|
||||
// Resources are Read using a kio.Reader function. Examples:
|
||||
// [kio.LocalPackageReader{}, kio.ByteReader{}]
|
||||
//
|
||||
// Resources read using a LocalPackageReader will have annotations applied so they can be
|
||||
// written back to the files they were read from.
|
||||
//
|
||||
// Modifying Resources
|
||||
//
|
||||
// Resources are modified using a kio.Filter. The kio.Filter accepts a collection of
|
||||
// Resources as input, and returns a new collection as output.
|
||||
// It is recommended to use the yaml package for manipulating individual Resources in
|
||||
// the collection.
|
||||
//
|
||||
// Writing Resources
|
||||
//
|
||||
// Resources are Read using a kio.Reader function. Examples:
|
||||
// [kio.LocalPackageWriter{}, kio.ByteWriter{}]
|
||||
//
|
||||
// ReadWriters
|
||||
//
|
||||
// It is preferred to use a ReadWriter when reading and writing from / to the same source.
|
||||
//
|
||||
// Building Pipelines
|
||||
//
|
||||
// The preferred way to transforms a collection of Resources is to use kio.Pipeline to Read,
|
||||
// Modify and Write the collection of Resources. Pipeline will automatically sequentially
|
||||
// invoke the Read, Modify, Write steps, returning and error immediately on any failure.
|
||||
package kio
|
||||
199
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/filters.go
generated
vendored
Normal file
199
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/filters.go
generated
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Filters are the list of known filters for unmarshalling a filter into a concrete
|
||||
// implementation.
|
||||
var Filters = map[string]func() kio.Filter{
|
||||
"FileSetter": func() kio.Filter { return &FileSetter{} },
|
||||
"FormatFilter": func() kio.Filter { return &FormatFilter{} },
|
||||
"GrepFilter": func() kio.Filter { return GrepFilter{} },
|
||||
"MatchModifier": func() kio.Filter { return &MatchModifyFilter{} },
|
||||
"Modifier": func() kio.Filter { return &Modifier{} },
|
||||
}
|
||||
|
||||
// filter wraps a kio.filter so that it can be unmarshalled from yaml.
|
||||
type KFilter struct {
|
||||
kio.Filter
|
||||
}
|
||||
|
||||
func (t KFilter) MarshalYAML() (interface{}, error) {
|
||||
return t.Filter, nil
|
||||
}
|
||||
|
||||
func (t *KFilter) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
i := map[string]interface{}{}
|
||||
if err := unmarshal(i); err != nil {
|
||||
return err
|
||||
}
|
||||
meta := &yaml.ResourceMeta{}
|
||||
if err := unmarshal(meta); err != nil {
|
||||
return err
|
||||
}
|
||||
filter, found := Filters[meta.Kind]
|
||||
if !found {
|
||||
var knownFilters []string
|
||||
for k := range Filters {
|
||||
knownFilters = append(knownFilters, k)
|
||||
}
|
||||
sort.Strings(knownFilters)
|
||||
return fmt.Errorf("unsupported filter Kind %v: may be one of: [%s]",
|
||||
meta, strings.Join(knownFilters, ","))
|
||||
}
|
||||
t.Filter = filter()
|
||||
|
||||
return unmarshal(t.Filter)
|
||||
}
|
||||
|
||||
// Modifier modifies the input Resources by invoking the provided pipeline.
|
||||
// Modifier will return any Resources for which the pipeline does not return an error.
|
||||
type Modifier struct {
|
||||
Kind string `yaml:"kind,omitempty"`
|
||||
|
||||
Filters yaml.YFilters `yaml:"pipeline,omitempty"`
|
||||
}
|
||||
|
||||
var _ kio.Filter = &Modifier{}
|
||||
|
||||
func (f Modifier) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
for i := range input {
|
||||
if _, err := input[i].Pipe(f.Filters.Filters()...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
type MatchModifyFilter struct {
|
||||
Kind string `yaml:"kind,omitempty"`
|
||||
|
||||
MatchFilters []yaml.YFilters `yaml:"match,omitempty"`
|
||||
|
||||
ModifyFilters yaml.YFilters `yaml:"modify,omitempty"`
|
||||
}
|
||||
|
||||
var _ kio.Filter = &MatchModifyFilter{}
|
||||
|
||||
func (f MatchModifyFilter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
var matches = input
|
||||
var err error
|
||||
for _, filter := range f.MatchFilters {
|
||||
matches, err = MatchFilter{Filters: filter}.Filter(matches)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
_, err = Modifier{Filters: f.ModifyFilters}.Filter(matches)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
type MatchFilter struct {
|
||||
Kind string `yaml:"kind,omitempty"`
|
||||
|
||||
Filters yaml.YFilters `yaml:"pipeline,omitempty"`
|
||||
}
|
||||
|
||||
var _ kio.Filter = &MatchFilter{}
|
||||
|
||||
func (f MatchFilter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
var output []*yaml.RNode
|
||||
for i := range input {
|
||||
if v, err := input[i].Pipe(f.Filters.Filters()...); err != nil {
|
||||
return nil, err
|
||||
} else if v == nil {
|
||||
continue
|
||||
}
|
||||
output = append(output, input[i])
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
type FilenameFmtVerb string
|
||||
|
||||
const (
|
||||
// KindFmt substitutes kind
|
||||
KindFmt FilenameFmtVerb = "%k"
|
||||
|
||||
// NameFmt substitutes metadata.name
|
||||
NameFmt FilenameFmtVerb = "%n"
|
||||
|
||||
// NamespaceFmt substitutes metdata.namespace
|
||||
NamespaceFmt FilenameFmtVerb = "%s"
|
||||
)
|
||||
|
||||
// FileSetter sets the file name and mode annotations on Resources.
|
||||
type FileSetter struct {
|
||||
Kind string `yaml:"kind,omitempty"`
|
||||
|
||||
// FilenamePattern is the pattern to use for generating filenames. FilenameFmtVerb
|
||||
// FielnameFmtVerbs may be specified to substitute Resource metadata into the filename.
|
||||
FilenamePattern string `yaml:"filenamePattern,omitempty"`
|
||||
|
||||
// Mode is the filemode to write.
|
||||
Mode string `yaml:"mode,omitempty"`
|
||||
|
||||
// Override will override the existing filename if it is set on the pattern.
|
||||
// Otherwise the existing filename is kept.
|
||||
Override bool `yaml:"override,omitempty"`
|
||||
}
|
||||
|
||||
var _ kio.Filter = &FileSetter{}
|
||||
|
||||
const DefaultFilenamePattern = "%n_%k.yaml"
|
||||
|
||||
func (f *FileSetter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
if f.Mode == "" {
|
||||
f.Mode = fmt.Sprintf("%d", 0600)
|
||||
}
|
||||
if f.FilenamePattern == "" {
|
||||
f.FilenamePattern = DefaultFilenamePattern
|
||||
}
|
||||
|
||||
resources := map[string][]*yaml.RNode{}
|
||||
for i := range input {
|
||||
m, err := input[i].GetMeta()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file := f.FilenamePattern
|
||||
file = strings.ReplaceAll(file, string(KindFmt), strings.ToLower(m.Kind))
|
||||
file = strings.ReplaceAll(file, string(NameFmt), strings.ToLower(m.Name))
|
||||
file = strings.ReplaceAll(file, string(NamespaceFmt), strings.ToLower(m.Namespace))
|
||||
|
||||
if _, found := m.Annotations[kioutil.PathAnnotation]; !found || f.Override {
|
||||
if _, err := input[i].Pipe(yaml.SetAnnotation(kioutil.PathAnnotation, file)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
resources[file] = append(resources[file], input[i])
|
||||
}
|
||||
|
||||
var output []*yaml.RNode
|
||||
for i := range resources {
|
||||
if err := kioutil.SortNodes(resources[i]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for j := range resources[i] {
|
||||
if _, err := resources[i][j].Pipe(
|
||||
yaml.SetAnnotation(kioutil.IndexAnnotation, fmt.Sprintf("%d", j))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
output = append(output, resources[i][j])
|
||||
}
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
314
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/fmtr.go
generated
vendored
Normal file
314
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/fmtr.go
generated
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package yamlfmt contains libraries for formatting yaml files containing
|
||||
// Kubernetes Resource configuration.
|
||||
//
|
||||
// Yaml files are formatted by:
|
||||
// - Sorting fields and map values
|
||||
// - Sorting unordered lists for whitelisted types
|
||||
// - Applying a canonical yaml Style
|
||||
//
|
||||
// Fields are ordered using a relative ordering applied to commonly
|
||||
// encountered Resource fields. All Resources, including non-builtin
|
||||
// Resources such as CRDs, share the same field precedence.
|
||||
//
|
||||
// Fields that do not appear in the explicit ordering are ordered
|
||||
// lexicographically.
|
||||
//
|
||||
// A subset of well known known unordered lists are sorted by element field
|
||||
// values.
|
||||
package filters
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type FormattingStrategy = string
|
||||
|
||||
const (
|
||||
// NoFmtAnnotation determines if the resource should be formatted.
|
||||
FmtAnnotation string = "config.kubernetes.io/formatting"
|
||||
|
||||
// FmtStrategyStandard means the resource will be formatted according
|
||||
// to the default rules.
|
||||
FmtStrategyStandard FormattingStrategy = "standard"
|
||||
|
||||
// FmtStrategyNone means the resource will not be formatted.
|
||||
FmtStrategyNone FormattingStrategy = "none"
|
||||
)
|
||||
|
||||
// FormatInput returns the formatted input.
|
||||
func FormatInput(input io.Reader) (*bytes.Buffer, error) {
|
||||
buff := &bytes.Buffer{}
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: input}},
|
||||
Filters: []kio.Filter{FormatFilter{}},
|
||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: buff}},
|
||||
}.Execute()
|
||||
|
||||
return buff, err
|
||||
}
|
||||
|
||||
// FormatFileOrDirectory reads the file or directory and formats each file's
|
||||
// contents by writing it back to the file.
|
||||
func FormatFileOrDirectory(path string) error {
|
||||
return kio.Pipeline{
|
||||
Inputs: []kio.Reader{kio.LocalPackageReader{
|
||||
PackagePath: path,
|
||||
}},
|
||||
Filters: []kio.Filter{FormatFilter{}},
|
||||
Outputs: []kio.Writer{kio.LocalPackageWriter{PackagePath: path}},
|
||||
}.Execute()
|
||||
}
|
||||
|
||||
type FormatFilter struct {
|
||||
Process func(n *yaml.Node) error
|
||||
UseSchema bool
|
||||
}
|
||||
|
||||
var _ kio.Filter = FormatFilter{}
|
||||
|
||||
func (f FormatFilter) Filter(slice []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
for i := range slice {
|
||||
fmtStrategy, err := getFormattingStrategy(slice[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if fmtStrategy == FmtStrategyNone {
|
||||
continue
|
||||
}
|
||||
|
||||
kindNode, err := slice[i].Pipe(yaml.Get("kind"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if kindNode == nil {
|
||||
continue
|
||||
}
|
||||
apiVersionNode, err := slice[i].Pipe(yaml.Get("apiVersion"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if apiVersionNode == nil {
|
||||
continue
|
||||
}
|
||||
kind, apiVersion := kindNode.YNode().Value, apiVersionNode.YNode().Value
|
||||
var s *openapi.ResourceSchema
|
||||
if f.UseSchema {
|
||||
s = openapi.SchemaForResourceType(yaml.TypeMeta{APIVersion: apiVersion, Kind: kind})
|
||||
} else {
|
||||
s = nil
|
||||
}
|
||||
err = (&formatter{apiVersion: apiVersion, kind: kind, process: f.Process}).
|
||||
fmtNode(slice[i].YNode(), "", s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return slice, nil
|
||||
}
|
||||
|
||||
// getFormattingStrategy looks for the formatting annotation to determine
|
||||
// which strategy should be used for formatting. The default is standard
|
||||
// if no annotation is found.
|
||||
func getFormattingStrategy(node *yaml.RNode) (FormattingStrategy, error) {
|
||||
value, err := node.Pipe(yaml.GetAnnotation(FmtAnnotation))
|
||||
if err != nil || value == nil {
|
||||
return FmtStrategyStandard, err
|
||||
}
|
||||
|
||||
fmtStrategy := value.YNode().Value
|
||||
|
||||
switch fmtStrategy {
|
||||
case FmtStrategyStandard:
|
||||
return FmtStrategyStandard, nil
|
||||
case FmtStrategyNone:
|
||||
return FmtStrategyNone, nil
|
||||
default:
|
||||
return "", fmt.Errorf(
|
||||
"formatting annotation has illegal value %s", fmtStrategy)
|
||||
}
|
||||
}
|
||||
|
||||
type formatter struct {
|
||||
apiVersion string
|
||||
kind string
|
||||
process func(n *yaml.Node) error
|
||||
}
|
||||
|
||||
// fmtNode recursively formats the Document Contents.
|
||||
// See: https://godoc.org/gopkg.in/yaml.v3#Node
|
||||
func (f *formatter) fmtNode(n *yaml.Node, path string, schema *openapi.ResourceSchema) error {
|
||||
if n.Kind == yaml.ScalarNode && schema != nil && schema.Schema != nil {
|
||||
// ensure values that are interpreted as non-string values (e.g. "true")
|
||||
// are properly quoted
|
||||
yaml.FormatNonStringStyle(n, *schema.Schema)
|
||||
}
|
||||
|
||||
// sort the order of mapping fields
|
||||
if n.Kind == yaml.MappingNode {
|
||||
sort.Sort(sortedMapContents(*n))
|
||||
}
|
||||
|
||||
// sort the order of sequence elements if it is whitelisted
|
||||
if n.Kind == yaml.SequenceNode {
|
||||
if yaml.WhitelistedListSortKinds.Has(f.kind) &&
|
||||
yaml.WhitelistedListSortApis.Has(f.apiVersion) {
|
||||
if sortField, found := yaml.WhitelistedListSortFields[path]; found {
|
||||
sort.Sort(sortedSeqContents{Node: *n, sortField: sortField})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// format the Content
|
||||
for i := range n.Content {
|
||||
// MappingNode are structured as having their fields as Content,
|
||||
// with the field-key and field-value alternating. e.g. Even elements
|
||||
// are the keys and odd elements are the values
|
||||
isFieldKey := n.Kind == yaml.MappingNode && i%2 == 0
|
||||
isFieldValue := n.Kind == yaml.MappingNode && i%2 == 1
|
||||
isElement := n.Kind == yaml.SequenceNode
|
||||
|
||||
// run the process callback on the node if it has been set
|
||||
// don't process keys: their format should be fixed
|
||||
if f.process != nil && !isFieldKey {
|
||||
if err := f.process(n.Content[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// get the schema for this Node
|
||||
p := path
|
||||
var s *openapi.ResourceSchema
|
||||
switch {
|
||||
case isFieldValue:
|
||||
// if the node is a field, lookup the schema using the field name
|
||||
p = fmt.Sprintf("%s.%s", path, n.Content[i-1].Value)
|
||||
if schema != nil {
|
||||
s = schema.Field(n.Content[i-1].Value)
|
||||
}
|
||||
case isElement:
|
||||
// if the node is a list element, lookup the schema for the array items
|
||||
if schema != nil {
|
||||
s = schema.Elements()
|
||||
}
|
||||
}
|
||||
// format the node using the schema
|
||||
err := f.fmtNode(n.Content[i], p, s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// sortedMapContents sorts the Contents field of a MappingNode by the field names using a statically
|
||||
// defined field precedence, and falling back on lexicographical sorting
|
||||
type sortedMapContents yaml.Node
|
||||
|
||||
func (s sortedMapContents) Len() int {
|
||||
return len(s.Content) / 2
|
||||
}
|
||||
func (s sortedMapContents) Swap(i, j int) {
|
||||
// yaml MappingNode Contents are a list of field names followed by
|
||||
// field values, rather than a list of field <name, value> pairs.
|
||||
// increment.
|
||||
//
|
||||
// e.g. ["field1Name", "field1Value", "field2Name", "field2Value"]
|
||||
iFieldNameIndex := i * 2
|
||||
jFieldNameIndex := j * 2
|
||||
iFieldValueIndex := iFieldNameIndex + 1
|
||||
jFieldValueIndex := jFieldNameIndex + 1
|
||||
|
||||
// swap field names
|
||||
s.Content[iFieldNameIndex], s.Content[jFieldNameIndex] =
|
||||
s.Content[jFieldNameIndex], s.Content[iFieldNameIndex]
|
||||
|
||||
// swap field values
|
||||
s.Content[iFieldValueIndex], s.Content[jFieldValueIndex] = s.
|
||||
Content[jFieldValueIndex], s.Content[iFieldValueIndex]
|
||||
}
|
||||
|
||||
func (s sortedMapContents) Less(i, j int) bool {
|
||||
iFieldNameIndex := i * 2
|
||||
jFieldNameIndex := j * 2
|
||||
iFieldName := s.Content[iFieldNameIndex].Value
|
||||
jFieldName := s.Content[jFieldNameIndex].Value
|
||||
|
||||
// order by their precedence values looked up from the index
|
||||
iOrder, foundI := yaml.FieldOrder[iFieldName]
|
||||
jOrder, foundJ := yaml.FieldOrder[jFieldName]
|
||||
if foundI && foundJ {
|
||||
return iOrder < jOrder
|
||||
}
|
||||
|
||||
// known fields come before unknown fields
|
||||
if foundI {
|
||||
return true
|
||||
}
|
||||
if foundJ {
|
||||
return false
|
||||
}
|
||||
|
||||
// neither field is known, sort them lexicographically
|
||||
return iFieldName < jFieldName
|
||||
}
|
||||
|
||||
// sortedSeqContents sorts the Contents field of a SequenceNode by the value of
|
||||
// the elements sortField.
|
||||
// e.g. it will sort spec.template.spec.containers by the value of the container `name` field
|
||||
type sortedSeqContents struct {
|
||||
yaml.Node
|
||||
sortField string
|
||||
}
|
||||
|
||||
func (s sortedSeqContents) Len() int {
|
||||
return len(s.Content)
|
||||
}
|
||||
func (s sortedSeqContents) Swap(i, j int) {
|
||||
s.Content[i], s.Content[j] = s.Content[j], s.Content[i]
|
||||
}
|
||||
func (s sortedSeqContents) Less(i, j int) bool {
|
||||
// primitive lists -- sort by the element's primitive values
|
||||
if s.sortField == "" {
|
||||
iValue := s.Content[i].Value
|
||||
jValue := s.Content[j].Value
|
||||
return iValue < jValue
|
||||
}
|
||||
|
||||
// map lists -- sort by the element's sortField values
|
||||
var iValue, jValue string
|
||||
for a := range s.Content[i].Content {
|
||||
if a%2 != 0 {
|
||||
continue // not a fieldNameIndex
|
||||
}
|
||||
// locate the index of the sortField field
|
||||
if s.Content[i].Content[a].Value == s.sortField {
|
||||
// a is the yaml node for the field key, a+1 is the node for the field value
|
||||
iValue = s.Content[i].Content[a+1].Value
|
||||
}
|
||||
}
|
||||
for a := range s.Content[j].Content {
|
||||
if a%2 != 0 {
|
||||
continue // not a fieldNameIndex
|
||||
}
|
||||
|
||||
// locate the index of the sortField field
|
||||
if s.Content[j].Content[a].Value == s.sortField {
|
||||
// a is the yaml node for the field key, a+1 is the node for the field value
|
||||
jValue = s.Content[j].Content[a+1].Value
|
||||
}
|
||||
}
|
||||
|
||||
// compare the field values
|
||||
return iValue < jValue
|
||||
}
|
||||
117
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/grep.go
generated
vendored
Normal file
117
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/grep.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filters
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type GrepType int
|
||||
|
||||
const (
|
||||
Regexp GrepType = 1 << iota
|
||||
GreaterThanEq
|
||||
GreaterThan
|
||||
LessThan
|
||||
LessThanEq
|
||||
)
|
||||
|
||||
// GrepFilter filters RNodes with a matching field
|
||||
type GrepFilter struct {
|
||||
Path []string `yaml:"path,omitempty"`
|
||||
Value string `yaml:"value,omitempty"`
|
||||
MatchType GrepType `yaml:"matchType,omitempty"`
|
||||
InvertMatch bool `yaml:"invertMatch,omitempty"`
|
||||
Compare func(a, b string) (int, error)
|
||||
}
|
||||
|
||||
var _ kio.Filter = GrepFilter{}
|
||||
|
||||
func (f GrepFilter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
// compile the regular expression 1 time if we are matching using regex
|
||||
var reg *regexp.Regexp
|
||||
var err error
|
||||
if f.MatchType == Regexp || f.MatchType == 0 {
|
||||
reg, err = regexp.Compile(f.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var output kio.ResourceNodeSlice
|
||||
for i := range input {
|
||||
node := input[i]
|
||||
val, err := node.Pipe(&yaml.PathMatcher{Path: f.Path})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if val == nil || len(val.Content()) == 0 {
|
||||
if f.InvertMatch {
|
||||
output = append(output, input[i])
|
||||
}
|
||||
continue
|
||||
}
|
||||
found := false
|
||||
err = val.VisitElements(func(elem *yaml.RNode) error {
|
||||
// get the value
|
||||
var str string
|
||||
if f.MatchType == Regexp {
|
||||
style := elem.YNode().Style
|
||||
defer func() { elem.YNode().Style = style }()
|
||||
elem.YNode().Style = yaml.FlowStyle
|
||||
str, err = elem.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
str = strings.TrimSpace(strings.ReplaceAll(str, `"`, ""))
|
||||
} else {
|
||||
// if not regexp, then it needs to parse into a quantity and comments will
|
||||
// break that
|
||||
str = elem.YNode().Value
|
||||
if str == "" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if f.MatchType == Regexp || f.MatchType == 0 {
|
||||
if reg.MatchString(str) {
|
||||
found = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
comp, err := f.Compare(str, f.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.MatchType == GreaterThan && comp > 0 {
|
||||
found = true
|
||||
}
|
||||
if f.MatchType == GreaterThanEq && comp >= 0 {
|
||||
found = true
|
||||
}
|
||||
if f.MatchType == LessThan && comp < 0 {
|
||||
found = true
|
||||
}
|
||||
if f.MatchType == LessThanEq && comp <= 0 {
|
||||
found = true
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if found == f.InvertMatch {
|
||||
continue
|
||||
}
|
||||
|
||||
output = append(output, input[i])
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
38
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/local.go
generated
vendored
Normal file
38
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/local.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filters
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
const LocalConfigAnnotation = "config.kubernetes.io/local-config"
|
||||
|
||||
// IsLocalConfig filters Resources using the config.kubernetes.io/local-config annotation
|
||||
type IsLocalConfig struct {
|
||||
// IncludeLocalConfig will include local-config if set to true
|
||||
IncludeLocalConfig bool `yaml:"includeLocalConfig,omitempty"`
|
||||
|
||||
// ExcludeNonLocalConfig will exclude non local-config if set to true
|
||||
ExcludeNonLocalConfig bool `yaml:"excludeNonLocalConfig,omitempty"`
|
||||
}
|
||||
|
||||
// Filter implements kio.Filter
|
||||
func (c *IsLocalConfig) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
var out []*yaml.RNode
|
||||
for i := range inputs {
|
||||
meta, err := inputs[i].GetMeta()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, local := meta.Annotations[LocalConfigAnnotation]
|
||||
|
||||
if local && c.IncludeLocalConfig {
|
||||
out = append(out, inputs[i])
|
||||
} else if !local && !c.ExcludeNonLocalConfig {
|
||||
out = append(out, inputs[i])
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
86
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/merge.go
generated
vendored
Normal file
86
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/merge.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package merge contains libraries for merging Resources and Patches
|
||||
package filters
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml/merge2"
|
||||
)
|
||||
|
||||
// MergeFilter merges Resources with the Group/Version/Kind/Namespace/Name together using
|
||||
// a 2-way merge strategy.
|
||||
//
|
||||
// - Fields set to null in the source will be cleared from the destination
|
||||
// - Fields with matching keys will be merged recursively
|
||||
// - Lists with an associative key (e.g. name) will have their elements merged using the key
|
||||
// - List without an associative key will have the dest list replaced by the source list
|
||||
type MergeFilter struct {
|
||||
Reverse bool
|
||||
}
|
||||
|
||||
var _ kio.Filter = MergeFilter{}
|
||||
|
||||
type mergeKey struct {
|
||||
apiVersion string
|
||||
kind string
|
||||
namespace string
|
||||
name string
|
||||
}
|
||||
|
||||
// MergeFilter implements kio.Filter by merging Resources with the same G/V/K/NS/N
|
||||
func (c MergeFilter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
// invert the merge precedence
|
||||
if c.Reverse {
|
||||
for i, j := 0, len(input)-1; i < j; i, j = i+1, j-1 {
|
||||
input[i], input[j] = input[j], input[i]
|
||||
}
|
||||
}
|
||||
|
||||
// index the Resources by G/V/K/NS/N
|
||||
index := map[mergeKey][]*yaml.RNode{}
|
||||
// retain the original ordering
|
||||
var order []mergeKey
|
||||
for i := range input {
|
||||
meta, err := input[i].GetMeta()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := mergeKey{
|
||||
apiVersion: meta.APIVersion,
|
||||
kind: meta.Kind,
|
||||
namespace: meta.Namespace,
|
||||
name: meta.Name,
|
||||
}
|
||||
if _, found := index[key]; !found {
|
||||
order = append(order, key)
|
||||
}
|
||||
index[key] = append(index[key], input[i])
|
||||
}
|
||||
|
||||
// merge each of the G/V/K/NS/N lists
|
||||
var output []*yaml.RNode
|
||||
var err error
|
||||
for _, k := range order {
|
||||
var merged *yaml.RNode
|
||||
resources := index[k]
|
||||
for i := range resources {
|
||||
patch := resources[i]
|
||||
if merged == nil {
|
||||
// first resources, don't merge it
|
||||
merged = resources[i]
|
||||
} else {
|
||||
merged, err = merge2.Merge(patch, merged, yaml.MergeOptions{
|
||||
ListIncreaseDirection: yaml.MergeOptionsListPrepend,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
output = append(output, merged)
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
203
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/merge3.go
generated
vendored
Normal file
203
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/merge3.go
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml/merge3"
|
||||
)
|
||||
|
||||
const (
|
||||
mergeSourceAnnotation = "config.kubernetes.io/merge-source"
|
||||
mergeSourceOriginal = "original"
|
||||
mergeSourceUpdated = "updated"
|
||||
mergeSourceDest = "dest"
|
||||
)
|
||||
|
||||
// Merge3 performs a 3-way merge on the original, updated, and destination packages.
|
||||
type Merge3 struct {
|
||||
OriginalPath string
|
||||
UpdatedPath string
|
||||
DestPath string
|
||||
MatchFilesGlob []string
|
||||
|
||||
// MergeOnPath will use the relative filepath as part of the merge key.
|
||||
// This may be necessary if the directory contains multiple copies of
|
||||
// the same resource, or resources patches.
|
||||
MergeOnPath bool
|
||||
}
|
||||
|
||||
func (m Merge3) Merge() error {
|
||||
// Read the destination package. The ReadWriter will take take of deleting files
|
||||
// for removed resources.
|
||||
var inputs []kio.Reader
|
||||
dest := &kio.LocalPackageReadWriter{
|
||||
PackagePath: m.DestPath,
|
||||
MatchFilesGlob: m.MatchFilesGlob,
|
||||
SetAnnotations: map[string]string{mergeSourceAnnotation: mergeSourceDest},
|
||||
}
|
||||
inputs = append(inputs, dest)
|
||||
|
||||
// Read the original package
|
||||
inputs = append(inputs, kio.LocalPackageReader{
|
||||
PackagePath: m.OriginalPath,
|
||||
MatchFilesGlob: m.MatchFilesGlob,
|
||||
SetAnnotations: map[string]string{mergeSourceAnnotation: mergeSourceOriginal},
|
||||
})
|
||||
|
||||
// Read the updated package
|
||||
inputs = append(inputs, kio.LocalPackageReader{
|
||||
PackagePath: m.UpdatedPath,
|
||||
MatchFilesGlob: m.MatchFilesGlob,
|
||||
SetAnnotations: map[string]string{mergeSourceAnnotation: mergeSourceUpdated},
|
||||
})
|
||||
|
||||
return kio.Pipeline{
|
||||
Inputs: inputs,
|
||||
Filters: []kio.Filter{m},
|
||||
Outputs: []kio.Writer{dest},
|
||||
}.Execute()
|
||||
}
|
||||
|
||||
// Filter combines Resources with the same GVK + N + NS into tuples, and then merges them
|
||||
func (m Merge3) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
// index the nodes by their identity
|
||||
tl := tuples{mergeOnPath: m.MergeOnPath}
|
||||
for i := range nodes {
|
||||
if err := tl.add(nodes[i]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over the inputs, merging as needed
|
||||
var output []*yaml.RNode
|
||||
for i := range tl.list {
|
||||
t := tl.list[i]
|
||||
switch {
|
||||
case t.original == nil && t.updated == nil && t.dest != nil:
|
||||
// added locally -- keep dest
|
||||
output = append(output, t.dest)
|
||||
case t.original == nil && t.updated != nil && t.dest == nil:
|
||||
// added in the update -- add update
|
||||
output = append(output, t.updated)
|
||||
case t.original != nil && t.updated == nil:
|
||||
// deleted in the update
|
||||
// don't include the resource in the output
|
||||
case t.original != nil && t.dest == nil:
|
||||
// deleted locally
|
||||
// don't include the resource in the output
|
||||
default:
|
||||
// dest and updated are non-nil -- merge them
|
||||
node, err := t.merge()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if node != nil {
|
||||
output = append(output, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// tuples combines nodes with the same GVK + N + NS
|
||||
type tuples struct {
|
||||
list []*tuple
|
||||
|
||||
// mergeOnPath if set to true will use the resource filepath
|
||||
// as part of the merge key
|
||||
mergeOnPath bool
|
||||
}
|
||||
|
||||
// isSameResource returns true if meta1 and meta2 are for the same logic resource
|
||||
func (ts *tuples) isSameResource(meta1, meta2 yaml.ResourceMeta) bool {
|
||||
if meta1.Name != meta2.Name {
|
||||
return false
|
||||
}
|
||||
if meta1.Namespace != meta2.Namespace {
|
||||
return false
|
||||
}
|
||||
if meta1.APIVersion != meta2.APIVersion {
|
||||
return false
|
||||
}
|
||||
if meta1.Kind != meta2.Kind {
|
||||
return false
|
||||
}
|
||||
if ts.mergeOnPath {
|
||||
// directories may contain multiple copies of a resource with the same
|
||||
// name, namespace, apiVersion and kind -- e.g. kustomize patches, or
|
||||
// multiple environments
|
||||
// mergeOnPath configures the merge logic to use the path as part of the
|
||||
// resource key
|
||||
if meta1.Annotations[kioutil.PathAnnotation] != meta2.Annotations[kioutil.PathAnnotation] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// add adds a node to the list, combining it with an existing matching Resource if found
|
||||
func (ts *tuples) add(node *yaml.RNode) error {
|
||||
nodeMeta, err := node.GetMeta()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range ts.list {
|
||||
t := ts.list[i]
|
||||
if ts.isSameResource(t.meta, nodeMeta) {
|
||||
return t.add(node)
|
||||
}
|
||||
}
|
||||
t := &tuple{meta: nodeMeta}
|
||||
if err := t.add(node); err != nil {
|
||||
return err
|
||||
}
|
||||
ts.list = append(ts.list, t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// tuple wraps an original, updated, and dest tuple for a given Resource
|
||||
type tuple struct {
|
||||
meta yaml.ResourceMeta
|
||||
original *yaml.RNode
|
||||
updated *yaml.RNode
|
||||
dest *yaml.RNode
|
||||
}
|
||||
|
||||
// add sets the corresponding tuple field for the node
|
||||
func (t *tuple) add(node *yaml.RNode) error {
|
||||
meta, err := node.GetMeta()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch meta.Annotations[mergeSourceAnnotation] {
|
||||
case mergeSourceDest:
|
||||
if t.dest != nil {
|
||||
return fmt.Errorf("dest source already specified")
|
||||
}
|
||||
t.dest = node
|
||||
case mergeSourceOriginal:
|
||||
if t.original != nil {
|
||||
return fmt.Errorf("original source already specified")
|
||||
}
|
||||
t.original = node
|
||||
case mergeSourceUpdated:
|
||||
if t.updated != nil {
|
||||
return fmt.Errorf("updated source already specified")
|
||||
}
|
||||
t.updated = node
|
||||
default:
|
||||
return fmt.Errorf("no source annotation for Resource")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// merge performs a 3-way merge on the tuple
|
||||
func (t *tuple) merge() (*yaml.RNode, error) {
|
||||
return merge3.Merge(t.dest, t.original, t.updated)
|
||||
}
|
||||
4
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/modify.go
generated
vendored
Normal file
4
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/modify.go
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filters
|
||||
32
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/stripcomments.go
generated
vendored
Normal file
32
vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/stripcomments.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filters
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type StripCommentsFilter struct{}
|
||||
|
||||
var _ kio.Filter = StripCommentsFilter{}
|
||||
|
||||
func (f StripCommentsFilter) Filter(slice []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
for i := range slice {
|
||||
stripComments(slice[i].YNode())
|
||||
}
|
||||
return slice, nil
|
||||
}
|
||||
|
||||
func stripComments(node *yaml.Node) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
node.HeadComment = ""
|
||||
node.LineComment = ""
|
||||
node.FootComment = ""
|
||||
for i := range node.Content {
|
||||
stripComments(node.Content[i])
|
||||
}
|
||||
}
|
||||
100
vendor/sigs.k8s.io/kustomize/kyaml/kio/ignorefilesmatcher.go
generated
vendored
Normal file
100
vendor/sigs.k8s.io/kustomize/kyaml/kio/ignorefilesmatcher.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kio
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/monochromegane/go-gitignore"
|
||||
"sigs.k8s.io/kustomize/kyaml/ext"
|
||||
)
|
||||
|
||||
// ignoreFilesMatcher handles `.krmignore` files, which allows for ignoring
|
||||
// files or folders in a package. The format of this file is a subset of the
|
||||
// gitignore format, with recursive patterns (like a/**/c) not supported. If a
|
||||
// file or folder matches any of the patterns in the .krmignore file for the
|
||||
// package, it will be excluded.
|
||||
//
|
||||
// It works as follows:
|
||||
//
|
||||
// * It will look for .krmignore file in the top folder and on the top level
|
||||
// of any subpackages. Subpackages are defined by the presence of a Krmfile
|
||||
// in the folder.
|
||||
// * `.krmignore` files only cover files and folders for the package in which
|
||||
// it is defined. So ignore patterns defined in a parent package does not
|
||||
// affect which files are ignored from a subpackage.
|
||||
// * An ignore pattern can not ignore a subpackage. So even if the parent
|
||||
// package contains a pattern that ignores the directory foo, if foo is a
|
||||
// subpackage, it will still be included if the IncludeSubpackages property
|
||||
// is set to true
|
||||
type ignoreFilesMatcher struct {
|
||||
matchers []matcher
|
||||
}
|
||||
|
||||
// readIgnoreFile checks whether there is a .krmignore file in the path, and
|
||||
// if it is, reads it in and turns it into a matcher. If we can't find a file,
|
||||
// we just add a matcher that match nothing.
|
||||
func (i *ignoreFilesMatcher) readIgnoreFile(path string) error {
|
||||
i.verifyPath(path)
|
||||
m, err := gitignore.NewGitIgnore(filepath.Join(path, ext.IgnoreFileName()))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
i.matchers = append(i.matchers, matcher{
|
||||
matcher: gitignore.DummyIgnoreMatcher(false),
|
||||
basePath: path,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
i.matchers = append(i.matchers, matcher{
|
||||
matcher: m,
|
||||
basePath: path,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyPath checks whether the top matcher on the stack
|
||||
// is correct for the provided filepath. Matchers are removed once
|
||||
// we encounter a filepath that is not a subpath of the basepath for
|
||||
// the matcher.
|
||||
func (i *ignoreFilesMatcher) verifyPath(path string) {
|
||||
for j := len(i.matchers) - 1; j >= 0; j-- {
|
||||
matcher := i.matchers[j]
|
||||
if strings.HasPrefix(path, matcher.basePath) || path == matcher.basePath {
|
||||
i.matchers = i.matchers[:j+1]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// matchFile checks whether the file given by the provided path matches
|
||||
// any of the patterns in the .krmignore file for the package.
|
||||
func (i *ignoreFilesMatcher) matchFile(path string) bool {
|
||||
if len(i.matchers) == 0 {
|
||||
return false
|
||||
}
|
||||
i.verifyPath(filepath.Dir(path))
|
||||
return i.matchers[len(i.matchers)-1].matcher.Match(path, false)
|
||||
}
|
||||
|
||||
// matchFile checks whether the directory given by the provided path matches
|
||||
// any of the patterns in the .krmignore file for the package.
|
||||
func (i *ignoreFilesMatcher) matchDir(path string) bool {
|
||||
if len(i.matchers) == 0 {
|
||||
return false
|
||||
}
|
||||
i.verifyPath(path)
|
||||
return i.matchers[len(i.matchers)-1].matcher.Match(path, true)
|
||||
}
|
||||
|
||||
// matcher wraps the gitignore matcher and the path to the folder
|
||||
// where the file was found.
|
||||
type matcher struct {
|
||||
matcher gitignore.IgnoreMatcher
|
||||
|
||||
basePath string
|
||||
}
|
||||
149
vendor/sigs.k8s.io/kustomize/kyaml/kio/kio.go
generated
vendored
Normal file
149
vendor/sigs.k8s.io/kustomize/kyaml/kio/kio.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package kio contains low-level libraries for reading, modifying and writing
|
||||
// Resource Configuration and packages.
|
||||
package kio
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Reader reads ResourceNodes. Analogous to io.Reader.
|
||||
type Reader interface {
|
||||
Read() ([]*yaml.RNode, error)
|
||||
}
|
||||
|
||||
// ResourceNodeSlice is a collection of ResourceNodes.
|
||||
// While ResourceNodeSlice has no inherent constraints on ordering or uniqueness, specific
|
||||
// Readers, Filters or Writers may have constraints.
|
||||
type ResourceNodeSlice []*yaml.RNode
|
||||
|
||||
var _ Reader = ResourceNodeSlice{}
|
||||
|
||||
func (o ResourceNodeSlice) Read() ([]*yaml.RNode, error) {
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// Writer writes ResourceNodes. Analogous to io.Writer.
|
||||
type Writer interface {
|
||||
Write([]*yaml.RNode) error
|
||||
}
|
||||
|
||||
// WriterFunc implements a Writer as a function.
|
||||
type WriterFunc func([]*yaml.RNode) error
|
||||
|
||||
func (fn WriterFunc) Write(o []*yaml.RNode) error {
|
||||
return fn(o)
|
||||
}
|
||||
|
||||
// ReaderWriter implements both Reader and Writer interfaces
|
||||
type ReaderWriter interface {
|
||||
Reader
|
||||
Writer
|
||||
}
|
||||
|
||||
// Filter modifies a collection of Resource Configuration by returning the modified slice.
|
||||
// When possible, Filters should be serializable to yaml so that they can be described
|
||||
// as either data or code.
|
||||
//
|
||||
// Analogous to http://www.linfo.org/filters.html
|
||||
type Filter interface {
|
||||
Filter([]*yaml.RNode) ([]*yaml.RNode, error)
|
||||
}
|
||||
|
||||
// FilterFunc implements a Filter as a function.
|
||||
type FilterFunc func([]*yaml.RNode) ([]*yaml.RNode, error)
|
||||
|
||||
func (fn FilterFunc) Filter(o []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return fn(o)
|
||||
}
|
||||
|
||||
// Pipeline reads Resource Configuration from a set of Inputs, applies some
|
||||
// transformation filters, and writes the results to a set of Outputs.
|
||||
//
|
||||
// Analogous to http://www.linfo.org/pipes.html
|
||||
type Pipeline struct {
|
||||
// Inputs provide sources for Resource Configuration to be read.
|
||||
Inputs []Reader `yaml:"inputs,omitempty"`
|
||||
|
||||
// Filters are transformations applied to the Resource Configuration.
|
||||
// They are applied in the order they are specified.
|
||||
// Analogous to http://www.linfo.org/filters.html
|
||||
Filters []Filter `yaml:"filters,omitempty"`
|
||||
|
||||
// Outputs are where the transformed Resource Configuration is written.
|
||||
Outputs []Writer `yaml:"outputs,omitempty"`
|
||||
|
||||
// ContinueOnEmptyResult configures what happens when a filter in the pipeline
|
||||
// returns an empty result.
|
||||
// If it is false (default), subsequent filters will be skipped and the result
|
||||
// will be returned immediately. This is useful as an optimization when you
|
||||
// know that subsequent filters will not alter the empty result.
|
||||
// If it is true, the empty result will be provided as input to the next
|
||||
// filter in the list. This is useful when subsequent functions in the
|
||||
// pipeline may generate new resources.
|
||||
ContinueOnEmptyResult bool `yaml:"continueOnEmptyResult,omitempty"`
|
||||
}
|
||||
|
||||
// Execute executes each step in the sequence, returning immediately after encountering
|
||||
// any error as part of the Pipeline.
|
||||
func (p Pipeline) Execute() error {
|
||||
return p.ExecuteWithCallback(nil)
|
||||
}
|
||||
|
||||
// PipelineExecuteCallbackFunc defines a callback function that will be called each time a step in the pipeline succeeds.
|
||||
type PipelineExecuteCallbackFunc = func(op Filter)
|
||||
|
||||
// ExecuteWithCallback executes each step in the sequence, returning immediately after encountering
|
||||
// any error as part of the Pipeline. The callback will be called each time a step succeeds.
|
||||
func (p Pipeline) ExecuteWithCallback(callback PipelineExecuteCallbackFunc) error {
|
||||
var result []*yaml.RNode
|
||||
|
||||
// read from the inputs
|
||||
for _, i := range p.Inputs {
|
||||
nodes, err := i.Read()
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
result = append(result, nodes...)
|
||||
}
|
||||
|
||||
// apply operations
|
||||
var err error
|
||||
for i := range p.Filters {
|
||||
op := p.Filters[i]
|
||||
if callback != nil {
|
||||
callback(op)
|
||||
}
|
||||
result, err = op.Filter(result)
|
||||
// TODO (issue 2872): This len(result) == 0 should be removed and empty result list should be
|
||||
// handled by outputs. However currently some writer like LocalPackageReadWriter
|
||||
// will clear the output directory and which will cause unpredictable results
|
||||
if len(result) == 0 && !p.ContinueOnEmptyResult || err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
// write to the outputs
|
||||
for _, o := range p.Outputs {
|
||||
if err := o.Write(result); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterAll runs the yaml.Filter against all inputs
|
||||
func FilterAll(filter yaml.Filter) Filter {
|
||||
return FilterFunc(func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
for i := range nodes {
|
||||
_, err := filter.Filter(nodes[i])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
return nodes, nil
|
||||
})
|
||||
}
|
||||
233
vendor/sigs.k8s.io/kustomize/kyaml/kio/kioutil/kioutil.go
generated
vendored
Normal file
233
vendor/sigs.k8s.io/kustomize/kyaml/kio/kioutil/kioutil.go
generated
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kioutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type AnnotationKey = string
|
||||
|
||||
const (
|
||||
// IndexAnnotation records the index of a specific resource in a file or input stream.
|
||||
IndexAnnotation AnnotationKey = "config.kubernetes.io/index"
|
||||
|
||||
// PathAnnotation records the path to the file the Resource was read from
|
||||
PathAnnotation AnnotationKey = "config.kubernetes.io/path"
|
||||
)
|
||||
|
||||
func GetFileAnnotations(rn *yaml.RNode) (string, string, error) {
|
||||
meta, err := rn.GetMeta()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
path := meta.Annotations[PathAnnotation]
|
||||
index := meta.Annotations[IndexAnnotation]
|
||||
return path, index, nil
|
||||
}
|
||||
|
||||
// ErrorIfMissingAnnotation validates the provided annotations are present on the given resources
|
||||
func ErrorIfMissingAnnotation(nodes []*yaml.RNode, keys ...AnnotationKey) error {
|
||||
for _, key := range keys {
|
||||
for _, node := range nodes {
|
||||
val, err := node.Pipe(yaml.GetAnnotation(key))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if val == nil {
|
||||
return errors.Errorf("missing annotation %s", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreatePathAnnotationValue creates a default path annotation value for a Resource.
|
||||
// The path prefix will be dir.
|
||||
func CreatePathAnnotationValue(dir string, m yaml.ResourceMeta) string {
|
||||
filename := fmt.Sprintf("%s_%s.yaml", strings.ToLower(m.Kind), m.Name)
|
||||
return path.Join(dir, m.Namespace, filename)
|
||||
}
|
||||
|
||||
// DefaultPathAndIndexAnnotation sets a default path or index value on any nodes missing the
|
||||
// annotation
|
||||
func DefaultPathAndIndexAnnotation(dir string, nodes []*yaml.RNode) error {
|
||||
counts := map[string]int{}
|
||||
|
||||
// check each node for the path annotation
|
||||
for i := range nodes {
|
||||
m, err := nodes[i].GetMeta()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// calculate the max index in each file in case we are appending
|
||||
if p, found := m.Annotations[PathAnnotation]; found {
|
||||
// record the max indexes into each file
|
||||
if i, found := m.Annotations[IndexAnnotation]; found {
|
||||
index, _ := strconv.Atoi(i)
|
||||
if index > counts[p] {
|
||||
counts[p] = index
|
||||
}
|
||||
}
|
||||
|
||||
// has the path annotation already -- do nothing
|
||||
continue
|
||||
}
|
||||
|
||||
// set a path annotation on the Resource
|
||||
path := CreatePathAnnotationValue(dir, m)
|
||||
if err := nodes[i].PipeE(yaml.SetAnnotation(PathAnnotation, path)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// set the index annotations
|
||||
for i := range nodes {
|
||||
m, err := nodes[i].GetMeta()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, found := m.Annotations[IndexAnnotation]; found {
|
||||
continue
|
||||
}
|
||||
|
||||
p := m.Annotations[PathAnnotation]
|
||||
|
||||
// set an index annotation on the Resource
|
||||
c := counts[p]
|
||||
counts[p] = c + 1
|
||||
if err := nodes[i].PipeE(
|
||||
yaml.SetAnnotation(IndexAnnotation, fmt.Sprintf("%d", c))); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultPathAnnotation sets a default path annotation on any Reources
|
||||
// missing it.
|
||||
func DefaultPathAnnotation(dir string, nodes []*yaml.RNode) error {
|
||||
// check each node for the path annotation
|
||||
for i := range nodes {
|
||||
m, err := nodes[i].GetMeta()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, found := m.Annotations[PathAnnotation]; found {
|
||||
// has the path annotation already -- do nothing
|
||||
continue
|
||||
}
|
||||
|
||||
// set a path annotation on the Resource
|
||||
path := CreatePathAnnotationValue(dir, m)
|
||||
if err := nodes[i].PipeE(yaml.SetAnnotation(PathAnnotation, path)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Map invokes fn for each element in nodes.
|
||||
func Map(nodes []*yaml.RNode, fn func(*yaml.RNode) (*yaml.RNode, error)) ([]*yaml.RNode, error) {
|
||||
var returnNodes []*yaml.RNode
|
||||
for i := range nodes {
|
||||
n, err := fn(nodes[i])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
if n != nil {
|
||||
returnNodes = append(returnNodes, n)
|
||||
}
|
||||
}
|
||||
return returnNodes, nil
|
||||
}
|
||||
|
||||
func MapMeta(nodes []*yaml.RNode, fn func(*yaml.RNode, yaml.ResourceMeta) (*yaml.RNode, error)) (
|
||||
[]*yaml.RNode, error) {
|
||||
var returnNodes []*yaml.RNode
|
||||
for i := range nodes {
|
||||
meta, err := nodes[i].GetMeta()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
n, err := fn(nodes[i], meta)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
if n != nil {
|
||||
returnNodes = append(returnNodes, n)
|
||||
}
|
||||
}
|
||||
return returnNodes, nil
|
||||
}
|
||||
|
||||
// SortNodes sorts nodes in place:
|
||||
// - by PathAnnotation annotation
|
||||
// - by IndexAnnotation annotation
|
||||
func SortNodes(nodes []*yaml.RNode) error {
|
||||
var err error
|
||||
// use stable sort to keep ordering of equal elements
|
||||
sort.SliceStable(nodes, func(i, j int) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
var iMeta, jMeta yaml.ResourceMeta
|
||||
if iMeta, _ = nodes[i].GetMeta(); err != nil {
|
||||
return false
|
||||
}
|
||||
if jMeta, _ = nodes[j].GetMeta(); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
iValue := iMeta.Annotations[PathAnnotation]
|
||||
jValue := jMeta.Annotations[PathAnnotation]
|
||||
if iValue != jValue {
|
||||
return iValue < jValue
|
||||
}
|
||||
|
||||
iValue = iMeta.Annotations[IndexAnnotation]
|
||||
jValue = jMeta.Annotations[IndexAnnotation]
|
||||
|
||||
// put resource config without an index first
|
||||
if iValue == jValue {
|
||||
return false
|
||||
}
|
||||
if iValue == "" {
|
||||
return true
|
||||
}
|
||||
if jValue == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// sort by index
|
||||
var iIndex, jIndex int
|
||||
iIndex, err = strconv.Atoi(iValue)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to parse config.kubernetes.io/index %s :%v", iValue, err)
|
||||
return false
|
||||
}
|
||||
jIndex, err = strconv.Atoi(jValue)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to parse config.kubernetes.io/index %s :%v", jValue, err)
|
||||
return false
|
||||
}
|
||||
if iIndex != jIndex {
|
||||
return iIndex < jIndex
|
||||
}
|
||||
|
||||
// elements are equal
|
||||
return false
|
||||
})
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
326
vendor/sigs.k8s.io/kustomize/kyaml/kio/pkgio_reader.go
generated
vendored
Normal file
326
vendor/sigs.k8s.io/kustomize/kyaml/kio/pkgio_reader.go
generated
vendored
Normal file
@@ -0,0 +1,326 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/sets"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// requiredResourcePackageAnnotations are annotations that are required to write resources back to
|
||||
// files.
|
||||
var requiredResourcePackageAnnotations = []string{kioutil.IndexAnnotation, kioutil.PathAnnotation}
|
||||
|
||||
// PackageBuffer implements Reader and Writer, storing Resources in a local field.
|
||||
type PackageBuffer struct {
|
||||
Nodes []*yaml.RNode
|
||||
}
|
||||
|
||||
func (r *PackageBuffer) Read() ([]*yaml.RNode, error) {
|
||||
return r.Nodes, nil
|
||||
}
|
||||
|
||||
func (r *PackageBuffer) Write(nodes []*yaml.RNode) error {
|
||||
r.Nodes = nodes
|
||||
return nil
|
||||
}
|
||||
|
||||
// LocalPackageReadWriter reads and writes Resources from / to a local directory.
|
||||
// When writing, LocalPackageReadWriter will delete files if all of the Resources from
|
||||
// that file have been removed from the output.
|
||||
type LocalPackageReadWriter struct {
|
||||
Kind string `yaml:"kind,omitempty"`
|
||||
|
||||
KeepReaderAnnotations bool `yaml:"keepReaderAnnotations,omitempty"`
|
||||
|
||||
// PackagePath is the path to the package directory.
|
||||
PackagePath string `yaml:"path,omitempty"`
|
||||
|
||||
// PackageFileName is the name of file containing package metadata.
|
||||
// It will be used to identify package.
|
||||
PackageFileName string `yaml:"packageFileName,omitempty"`
|
||||
|
||||
// MatchFilesGlob configures Read to only read Resources from files matching any of the
|
||||
// provided patterns.
|
||||
// Defaults to ["*.yaml", "*.yml"] if empty. To match all files specify ["*"].
|
||||
MatchFilesGlob []string `yaml:"matchFilesGlob,omitempty"`
|
||||
|
||||
// IncludeSubpackages will configure Read to read Resources from subpackages.
|
||||
// Subpackages are identified by presence of PackageFileName.
|
||||
IncludeSubpackages bool `yaml:"includeSubpackages,omitempty"`
|
||||
|
||||
// ErrorIfNonResources will configure Read to throw an error if yaml missing missing
|
||||
// apiVersion or kind is read.
|
||||
ErrorIfNonResources bool `yaml:"errorIfNonResources,omitempty"`
|
||||
|
||||
// OmitReaderAnnotations will cause the reader to skip annotating Resources with the file
|
||||
// path and mode.
|
||||
OmitReaderAnnotations bool `yaml:"omitReaderAnnotations,omitempty"`
|
||||
|
||||
// SetAnnotations are annotations to set on the Resources as they are read.
|
||||
SetAnnotations map[string]string `yaml:"setAnnotations,omitempty"`
|
||||
|
||||
// NoDeleteFiles if set to true, LocalPackageReadWriter won't delete any files
|
||||
NoDeleteFiles bool `yaml:"noDeleteFiles,omitempty"`
|
||||
|
||||
files sets.String
|
||||
|
||||
// FileSkipFunc is a function which returns true if reader should ignore
|
||||
// the file
|
||||
FileSkipFunc LocalPackageSkipFileFunc
|
||||
}
|
||||
|
||||
func (r *LocalPackageReadWriter) Read() ([]*yaml.RNode, error) {
|
||||
nodes, err := LocalPackageReader{
|
||||
PackagePath: r.PackagePath,
|
||||
MatchFilesGlob: r.MatchFilesGlob,
|
||||
IncludeSubpackages: r.IncludeSubpackages,
|
||||
ErrorIfNonResources: r.ErrorIfNonResources,
|
||||
SetAnnotations: r.SetAnnotations,
|
||||
PackageFileName: r.PackageFileName,
|
||||
FileSkipFunc: r.FileSkipFunc,
|
||||
}.Read()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
// keep track of all the files
|
||||
if !r.NoDeleteFiles {
|
||||
r.files, err = r.getFiles(nodes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func (r *LocalPackageReadWriter) Write(nodes []*yaml.RNode) error {
|
||||
newFiles, err := r.getFiles(nodes)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
var clear []string
|
||||
for k := range r.SetAnnotations {
|
||||
clear = append(clear, k)
|
||||
}
|
||||
err = LocalPackageWriter{
|
||||
PackagePath: r.PackagePath,
|
||||
ClearAnnotations: clear,
|
||||
KeepReaderAnnotations: r.KeepReaderAnnotations,
|
||||
}.Write(nodes)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
deleteFiles := r.files.Difference(newFiles)
|
||||
for f := range deleteFiles {
|
||||
if err = os.Remove(filepath.Join(r.PackagePath, f)); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *LocalPackageReadWriter) getFiles(nodes []*yaml.RNode) (sets.String, error) {
|
||||
val := sets.String{}
|
||||
for _, n := range nodes {
|
||||
path, _, err := kioutil.GetFileAnnotations(n)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
val.Insert(path)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// LocalPackageSkipFileFunc is a function which returns true if the file
|
||||
// in the package should be ignored by reader.
|
||||
// relPath is an OS specific relative path
|
||||
type LocalPackageSkipFileFunc func(relPath string) bool
|
||||
|
||||
// LocalPackageReader reads ResourceNodes from a local package.
|
||||
type LocalPackageReader struct {
|
||||
Kind string `yaml:"kind,omitempty"`
|
||||
|
||||
// PackagePath is the path to the package directory.
|
||||
PackagePath string `yaml:"path,omitempty"`
|
||||
|
||||
// PackageFileName is the name of file containing package metadata.
|
||||
// It will be used to identify package.
|
||||
PackageFileName string `yaml:"packageFileName,omitempty"`
|
||||
|
||||
// MatchFilesGlob configures Read to only read Resources from files matching any of the
|
||||
// provided patterns.
|
||||
// Defaults to ["*.yaml", "*.yml"] if empty. To match all files specify ["*"].
|
||||
MatchFilesGlob []string `yaml:"matchFilesGlob,omitempty"`
|
||||
|
||||
// IncludeSubpackages will configure Read to read Resources from subpackages.
|
||||
// Subpackages are identified by presence of PackageFileName.
|
||||
IncludeSubpackages bool `yaml:"includeSubpackages,omitempty"`
|
||||
|
||||
// ErrorIfNonResources will configure Read to throw an error if yaml missing missing
|
||||
// apiVersion or kind is read.
|
||||
ErrorIfNonResources bool `yaml:"errorIfNonResources,omitempty"`
|
||||
|
||||
// OmitReaderAnnotations will cause the reader to skip annotating Resources with the file
|
||||
// path and mode.
|
||||
OmitReaderAnnotations bool `yaml:"omitReaderAnnotations,omitempty"`
|
||||
|
||||
// SetAnnotations are annotations to set on the Resources as they are read.
|
||||
SetAnnotations map[string]string `yaml:"setAnnotations,omitempty"`
|
||||
|
||||
// FileSkipFunc is a function which returns true if reader should ignore
|
||||
// the file
|
||||
FileSkipFunc LocalPackageSkipFileFunc
|
||||
}
|
||||
|
||||
var _ Reader = LocalPackageReader{}
|
||||
|
||||
var DefaultMatch = []string{"*.yaml", "*.yml"}
|
||||
var JSONMatch = []string{"*.json"}
|
||||
var MatchAll = append(DefaultMatch, JSONMatch...)
|
||||
|
||||
// Read reads the Resources.
|
||||
func (r LocalPackageReader) Read() ([]*yaml.RNode, error) {
|
||||
if r.PackagePath == "" {
|
||||
return nil, fmt.Errorf("must specify package path")
|
||||
}
|
||||
|
||||
// use slash for path
|
||||
r.PackagePath = filepath.ToSlash(r.PackagePath)
|
||||
if len(r.MatchFilesGlob) == 0 {
|
||||
r.MatchFilesGlob = DefaultMatch
|
||||
}
|
||||
|
||||
var operand ResourceNodeSlice
|
||||
var pathRelativeTo string
|
||||
var err error
|
||||
ignoreFilesMatcher := &ignoreFilesMatcher{}
|
||||
r.PackagePath, err = filepath.Abs(r.PackagePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
err = filepath.Walk(r.PackagePath, func(
|
||||
path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// is this the user specified path?
|
||||
if path == r.PackagePath {
|
||||
if info.IsDir() {
|
||||
// skip the root package directory, but check for a
|
||||
// .krmignore file first.
|
||||
pathRelativeTo = r.PackagePath
|
||||
return ignoreFilesMatcher.readIgnoreFile(path)
|
||||
}
|
||||
|
||||
// user specified path is a file rather than a directory.
|
||||
// make its path relative to its parent so it can be written to another file.
|
||||
pathRelativeTo = filepath.Dir(r.PackagePath)
|
||||
}
|
||||
|
||||
// check if we should skip the directory or file
|
||||
if info.IsDir() {
|
||||
return r.shouldSkipDir(path, ignoreFilesMatcher)
|
||||
}
|
||||
|
||||
// get the relative path to file within the package so we can write the files back out
|
||||
// to another location.
|
||||
relPath, err := filepath.Rel(pathRelativeTo, path)
|
||||
if err != nil {
|
||||
return errors.WrapPrefixf(err, pathRelativeTo)
|
||||
}
|
||||
if match, err := r.shouldSkipFile(path, relPath, ignoreFilesMatcher); err != nil {
|
||||
return err
|
||||
} else if match {
|
||||
// skip this file
|
||||
return nil
|
||||
}
|
||||
|
||||
r.initReaderAnnotations(relPath, info)
|
||||
nodes, err := r.readFile(path, info)
|
||||
if err != nil {
|
||||
return errors.WrapPrefixf(err, path)
|
||||
}
|
||||
operand = append(operand, nodes...)
|
||||
return nil
|
||||
})
|
||||
return operand, err
|
||||
}
|
||||
|
||||
// readFile reads the ResourceNodes from a file
|
||||
func (r *LocalPackageReader) readFile(path string, _ os.FileInfo) ([]*yaml.RNode, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
rr := &ByteReader{
|
||||
DisableUnwrapping: true,
|
||||
Reader: f,
|
||||
OmitReaderAnnotations: r.OmitReaderAnnotations,
|
||||
SetAnnotations: r.SetAnnotations,
|
||||
}
|
||||
return rr.Read()
|
||||
}
|
||||
|
||||
// shouldSkipFile returns true if the file should be skipped
|
||||
func (r *LocalPackageReader) shouldSkipFile(path, relPath string, matcher *ignoreFilesMatcher) (bool, error) {
|
||||
// check if the file is covered by a .krmignore file.
|
||||
if matcher.matchFile(path) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if r.FileSkipFunc != nil && r.FileSkipFunc(relPath) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// check if the files are in scope
|
||||
for _, g := range r.MatchFilesGlob {
|
||||
if match, err := filepath.Match(g, filepath.Base(path)); err != nil {
|
||||
return true, errors.Wrap(err)
|
||||
} else if match {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// initReaderAnnotations adds the LocalPackageReader Annotations to r.SetAnnotations
|
||||
func (r *LocalPackageReader) initReaderAnnotations(path string, _ os.FileInfo) {
|
||||
if r.SetAnnotations == nil {
|
||||
r.SetAnnotations = map[string]string{}
|
||||
}
|
||||
if !r.OmitReaderAnnotations {
|
||||
r.SetAnnotations[kioutil.PathAnnotation] = path
|
||||
}
|
||||
}
|
||||
|
||||
// shouldSkipDir returns a filepath.SkipDir if the directory should be skipped
|
||||
func (r *LocalPackageReader) shouldSkipDir(path string, matcher *ignoreFilesMatcher) error {
|
||||
if matcher.matchDir(path) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
if r.PackageFileName == "" {
|
||||
return nil
|
||||
}
|
||||
// check if this is a subpackage
|
||||
_, err := os.Stat(filepath.Join(path, r.PackageFileName))
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if !r.IncludeSubpackages {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return matcher.readIgnoreFile(path)
|
||||
}
|
||||
152
vendor/sigs.k8s.io/kustomize/kyaml/kio/pkgio_writer.go
generated
vendored
Normal file
152
vendor/sigs.k8s.io/kustomize/kyaml/kio/pkgio_writer.go
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// LocalPackageWriter writes ResourceNodes to a filesystem
|
||||
type LocalPackageWriter struct {
|
||||
Kind string `yaml:"kind,omitempty"`
|
||||
|
||||
// PackagePath is the path to the package directory.
|
||||
PackagePath string `yaml:"path,omitempty"`
|
||||
|
||||
// KeepReaderAnnotations if set will retain the annotations set by LocalPackageReader
|
||||
KeepReaderAnnotations bool `yaml:"keepReaderAnnotations,omitempty"`
|
||||
|
||||
// ClearAnnotations will clear annotations before writing the resources
|
||||
ClearAnnotations []string `yaml:"clearAnnotations,omitempty"`
|
||||
}
|
||||
|
||||
var _ Writer = LocalPackageWriter{}
|
||||
|
||||
func (r LocalPackageWriter) Write(nodes []*yaml.RNode) error {
|
||||
// set the path and index annotations if they are missing
|
||||
if err := kioutil.DefaultPathAndIndexAnnotation("", nodes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s, err := os.Stat(r.PackagePath); err != nil {
|
||||
return err
|
||||
} else if !s.IsDir() {
|
||||
// if the user specified input isn't a directory, the package is the directory of the
|
||||
// target
|
||||
r.PackagePath = filepath.Dir(r.PackagePath)
|
||||
}
|
||||
|
||||
// setup indexes for writing Resources back to files
|
||||
if err := r.errorIfMissingRequiredAnnotation(nodes); err != nil {
|
||||
return err
|
||||
}
|
||||
outputFiles, err := r.indexByFilePath(nodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k := range outputFiles {
|
||||
if err = kioutil.SortNodes(outputFiles[k]); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
if !r.KeepReaderAnnotations {
|
||||
r.ClearAnnotations = append(r.ClearAnnotations, kioutil.PathAnnotation)
|
||||
}
|
||||
|
||||
// validate outputs before writing any
|
||||
for path := range outputFiles {
|
||||
outputPath := filepath.Join(r.PackagePath, path)
|
||||
if st, err := os.Stat(outputPath); !os.IsNotExist(err) {
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if st.IsDir() {
|
||||
return fmt.Errorf("config.kubernetes.io/path cannot be a directory: %s", path)
|
||||
}
|
||||
}
|
||||
|
||||
err = os.MkdirAll(filepath.Dir(outputPath), 0700)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
// write files
|
||||
for path := range outputFiles {
|
||||
outputPath := filepath.Join(r.PackagePath, path)
|
||||
err = os.MkdirAll(filepath.Dir(filepath.Join(r.PackagePath, path)), 0700)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(outputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(0600))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if err := func() error {
|
||||
defer f.Close()
|
||||
w := ByteWriter{
|
||||
Writer: f,
|
||||
KeepReaderAnnotations: r.KeepReaderAnnotations,
|
||||
ClearAnnotations: r.ClearAnnotations,
|
||||
}
|
||||
if err = w.Write(outputFiles[path]); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}(); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r LocalPackageWriter) errorIfMissingRequiredAnnotation(nodes []*yaml.RNode) error {
|
||||
for i := range nodes {
|
||||
for _, s := range requiredResourcePackageAnnotations {
|
||||
key, err := nodes[i].Pipe(yaml.GetAnnotation(s))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if key == nil || key.YNode() == nil || key.YNode().Value == "" {
|
||||
return errors.Errorf(
|
||||
"resources must be annotated with %s to be written to files", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r LocalPackageWriter) indexByFilePath(nodes []*yaml.RNode) (map[string][]*yaml.RNode, error) {
|
||||
outputFiles := map[string][]*yaml.RNode{}
|
||||
for i := range nodes {
|
||||
// parse the file write path
|
||||
node := nodes[i]
|
||||
value, err := node.Pipe(yaml.GetAnnotation(kioutil.PathAnnotation))
|
||||
if err != nil {
|
||||
// this should never happen if errorIfMissingRequiredAnnotation was run
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
path := value.YNode().Value
|
||||
outputFiles[path] = append(outputFiles[path], node)
|
||||
|
||||
if filepath.IsAbs(path) {
|
||||
return nil, errors.Errorf("package paths may not be absolute paths")
|
||||
}
|
||||
if strings.Contains(filepath.Clean(path), "..") {
|
||||
return nil, fmt.Errorf("resource must be written under package %s: %s",
|
||||
r.PackagePath, filepath.Clean(path))
|
||||
}
|
||||
}
|
||||
return outputFiles, nil
|
||||
}
|
||||
55
vendor/sigs.k8s.io/kustomize/kyaml/kio/testing.go
generated
vendored
Normal file
55
vendor/sigs.k8s.io/kustomize/kyaml/kio/testing.go
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kio
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Setup creates directories and files for testing
|
||||
type Setup struct {
|
||||
// root is the tmp directory
|
||||
Root string
|
||||
}
|
||||
|
||||
// setupDirectories creates directories for reading test configuration from
|
||||
func SetupDirectories(t *testing.T, dirs ...string) Setup {
|
||||
d, err := ioutil.TempDir("", "kyaml-test")
|
||||
if !assert.NoError(t, err) {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
err = os.Chdir(d)
|
||||
if !assert.NoError(t, err) {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
for _, s := range dirs {
|
||||
err = os.MkdirAll(s, 0700)
|
||||
if !assert.NoError(t, err) {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
}
|
||||
return Setup{Root: d}
|
||||
}
|
||||
|
||||
// writeFile writes a file under the test directory
|
||||
func (s Setup) WriteFile(t *testing.T, path string, value []byte) {
|
||||
err := os.MkdirAll(filepath.Dir(filepath.Join(s.Root, path)), 0700)
|
||||
if !assert.NoError(t, err) {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(s.Root, path), value, 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
assert.FailNow(t, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// clean deletes the test config
|
||||
func (s Setup) Clean() {
|
||||
os.RemoveAll(s.Root)
|
||||
}
|
||||
514
vendor/sigs.k8s.io/kustomize/kyaml/kio/tree.go
generated
vendored
Normal file
514
vendor/sigs.k8s.io/kustomize/kyaml/kio/tree.go
generated
vendored
Normal file
@@ -0,0 +1,514 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/xlab/treeprint"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type TreeStructure string
|
||||
|
||||
const (
|
||||
// TreeStructurePackage configures TreeWriter to generate the tree structure off of the
|
||||
// Resources packages.
|
||||
TreeStructurePackage TreeStructure = "directory"
|
||||
|
||||
// TreeStructureOwners configures TreeWriter to generate the tree structure off of the
|
||||
// Resource owners.
|
||||
TreeStructureGraph TreeStructure = "owners"
|
||||
)
|
||||
|
||||
var GraphStructures = []string{string(TreeStructureGraph), string(TreeStructurePackage)}
|
||||
|
||||
// TreeWriter prints the package structured as a tree.
|
||||
// TODO(pwittrock): test this package better. it is lower-risk since it is only
|
||||
// used for printing rather than updating or editing.
|
||||
type TreeWriter struct {
|
||||
Writer io.Writer
|
||||
Root string
|
||||
Fields []TreeWriterField
|
||||
Structure TreeStructure
|
||||
OpenAPIFileName string
|
||||
}
|
||||
|
||||
// TreeWriterField configures a Resource field to be included in the tree
|
||||
type TreeWriterField struct {
|
||||
yaml.PathMatcher
|
||||
Name string
|
||||
SubName string
|
||||
}
|
||||
|
||||
func (p TreeWriter) packageStructure(nodes []*yaml.RNode) error {
|
||||
indexByPackage := p.index(nodes)
|
||||
|
||||
// create the new tree
|
||||
tree := treeprint.New()
|
||||
tree.SetValue(p.Root)
|
||||
|
||||
// add each package to the tree
|
||||
treeIndex := map[string]treeprint.Tree{}
|
||||
keys := p.sort(indexByPackage)
|
||||
for _, pkg := range keys {
|
||||
// create a branch for this package -- search for the parent package and create
|
||||
// the branch under it -- requires that the keys are sorted
|
||||
branch := tree
|
||||
for parent, subTree := range treeIndex {
|
||||
if strings.HasPrefix(pkg, parent) {
|
||||
// found a package whose path is a prefix to our own, use this
|
||||
// package if a closer one isn't found
|
||||
branch = subTree
|
||||
// don't break, continue searching for more closely related ancestors
|
||||
}
|
||||
}
|
||||
|
||||
// create a new branch for the package
|
||||
createOk := pkg != "." // special edge case logic for tree on current working dir
|
||||
if createOk {
|
||||
branch = branch.AddBranch(branchName(p.Root, pkg, p.OpenAPIFileName))
|
||||
}
|
||||
|
||||
// cache the branch for this package
|
||||
treeIndex[pkg] = branch
|
||||
|
||||
// print each resource in the package
|
||||
for i := range indexByPackage[pkg] {
|
||||
var err error
|
||||
if _, err = p.doResource(indexByPackage[pkg][i], "", branch); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, err := io.WriteString(p.Writer, tree.String())
|
||||
return err
|
||||
}
|
||||
|
||||
// branchName takes the root directory and relative path to the directory
|
||||
// and returns the branch name
|
||||
func branchName(root, dirRelPath, openAPIFileName string) string {
|
||||
name := filepath.Base(dirRelPath)
|
||||
_, err := os.Stat(filepath.Join(root, dirRelPath, openAPIFileName))
|
||||
if !os.IsNotExist(err) {
|
||||
// add Pkg: prefix indicating that it is a separate package as it has
|
||||
// openAPIFile
|
||||
return fmt.Sprintf("Pkg: %s", name)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// Write writes the ascii tree to p.Writer
|
||||
func (p TreeWriter) Write(nodes []*yaml.RNode) error {
|
||||
switch p.Structure {
|
||||
case TreeStructurePackage:
|
||||
return p.packageStructure(nodes)
|
||||
case TreeStructureGraph:
|
||||
return p.graphStructure(nodes)
|
||||
}
|
||||
|
||||
// If any resource has an owner reference, default to the graph structure. Otherwise, use package structure.
|
||||
for _, node := range nodes {
|
||||
if owners, _ := node.Pipe(yaml.Lookup("metadata", "ownerReferences")); owners != nil {
|
||||
return p.graphStructure(nodes)
|
||||
}
|
||||
}
|
||||
return p.packageStructure(nodes)
|
||||
}
|
||||
|
||||
// node wraps a tree node, and any children nodes
|
||||
type node struct {
|
||||
p TreeWriter
|
||||
*yaml.RNode
|
||||
children []*node
|
||||
}
|
||||
|
||||
func (a node) Len() int { return len(a.children) }
|
||||
func (a node) Swap(i, j int) { a.children[i], a.children[j] = a.children[j], a.children[i] }
|
||||
func (a node) Less(i, j int) bool {
|
||||
return compareNodes(a.children[i].RNode, a.children[j].RNode)
|
||||
}
|
||||
|
||||
// Tree adds this node to the root
|
||||
func (a node) Tree(root treeprint.Tree) error {
|
||||
sort.Sort(a)
|
||||
branch := root
|
||||
var err error
|
||||
|
||||
// generate a node for the Resource
|
||||
if a.RNode != nil {
|
||||
branch, err = a.p.doResource(a.RNode, "Resource", root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// attach children to the branch
|
||||
for _, n := range a.children {
|
||||
if err := n.Tree(branch); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// graphStructure writes the tree using owners for structure
|
||||
func (p TreeWriter) graphStructure(nodes []*yaml.RNode) error {
|
||||
resourceToOwner := map[string]*node{}
|
||||
root := &node{}
|
||||
// index each of the nodes by their owner
|
||||
for _, n := range nodes {
|
||||
ownerVal, err := ownerToString(n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var owner *node
|
||||
if ownerVal == "" {
|
||||
// no owner -- attach to the root
|
||||
owner = root
|
||||
} else {
|
||||
// owner found -- attach to the owner
|
||||
var found bool
|
||||
owner, found = resourceToOwner[ownerVal]
|
||||
if !found {
|
||||
// initialize the owner if not found
|
||||
resourceToOwner[ownerVal] = &node{p: p}
|
||||
owner = resourceToOwner[ownerVal]
|
||||
}
|
||||
}
|
||||
|
||||
nodeVal, err := nodeToString(n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
val, found := resourceToOwner[nodeVal]
|
||||
if !found {
|
||||
// initialize the node if not found -- may have already been initialized if it
|
||||
// is the owner of another node
|
||||
resourceToOwner[nodeVal] = &node{p: p}
|
||||
val = resourceToOwner[nodeVal]
|
||||
}
|
||||
val.RNode = n
|
||||
owner.children = append(owner.children, val)
|
||||
}
|
||||
|
||||
for k, v := range resourceToOwner {
|
||||
if v.RNode == nil {
|
||||
return fmt.Errorf(
|
||||
"owner '%s' not found in input, but found as an owner of input objects", k)
|
||||
}
|
||||
}
|
||||
|
||||
// print the tree
|
||||
tree := treeprint.New()
|
||||
if err := root.Tree(tree); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := io.WriteString(p.Writer, tree.String())
|
||||
return err
|
||||
}
|
||||
|
||||
// nodeToString generates a string to identify the node -- matches ownerToString format
|
||||
func nodeToString(node *yaml.RNode) (string, error) {
|
||||
meta, err := node.GetMeta()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %s/%s", meta.Kind, meta.Namespace, meta.Name), nil
|
||||
}
|
||||
|
||||
// ownerToString generate a string to identify the owner -- matches nodeToString format
|
||||
func ownerToString(node *yaml.RNode) (string, error) {
|
||||
meta, err := node.GetMeta()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
namespace := meta.Namespace
|
||||
|
||||
owners, err := node.Pipe(yaml.Lookup("metadata", "ownerReferences"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if owners == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
elements, err := owners.Elements()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(elements) == 0 {
|
||||
return "", err
|
||||
}
|
||||
owner := elements[0]
|
||||
var kind, name string
|
||||
|
||||
if value := owner.Field("kind"); !value.IsNilOrEmpty() {
|
||||
kind = value.Value.YNode().Value
|
||||
}
|
||||
if value := owner.Field("name"); !value.IsNilOrEmpty() {
|
||||
name = value.Value.YNode().Value
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %s/%s", kind, namespace, name), nil
|
||||
}
|
||||
|
||||
// index indexes the Resources by their package
|
||||
func (p TreeWriter) index(nodes []*yaml.RNode) map[string][]*yaml.RNode {
|
||||
// index the ResourceNodes by package
|
||||
indexByPackage := map[string][]*yaml.RNode{}
|
||||
for i := range nodes {
|
||||
meta, err := nodes[i].GetMeta()
|
||||
if err != nil || meta.Kind == "" {
|
||||
// not a resource
|
||||
continue
|
||||
}
|
||||
pkg := filepath.Dir(meta.Annotations[kioutil.PathAnnotation])
|
||||
indexByPackage[pkg] = append(indexByPackage[pkg], nodes[i])
|
||||
}
|
||||
return indexByPackage
|
||||
}
|
||||
|
||||
func compareNodes(i, j *yaml.RNode) bool {
|
||||
metai, _ := i.GetMeta()
|
||||
metaj, _ := j.GetMeta()
|
||||
pi := metai.Annotations[kioutil.PathAnnotation]
|
||||
pj := metaj.Annotations[kioutil.PathAnnotation]
|
||||
|
||||
// compare file names
|
||||
if filepath.Base(pi) != filepath.Base(pj) {
|
||||
return filepath.Base(pi) < filepath.Base(pj)
|
||||
}
|
||||
|
||||
// compare namespace
|
||||
if metai.Namespace != metaj.Namespace {
|
||||
return metai.Namespace < metaj.Namespace
|
||||
}
|
||||
|
||||
// compare name
|
||||
if metai.Name != metaj.Name {
|
||||
return metai.Name < metaj.Name
|
||||
}
|
||||
|
||||
// compare kind
|
||||
if metai.Kind != metaj.Kind {
|
||||
return metai.Kind < metaj.Kind
|
||||
}
|
||||
|
||||
// compare apiVersion
|
||||
if metai.APIVersion != metaj.APIVersion {
|
||||
return metai.APIVersion < metaj.APIVersion
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// sort sorts the Resources in the index in display order and returns the ordered
|
||||
// keys for the index
|
||||
//
|
||||
// Packages are sorted by package name
|
||||
// Resources within a package are sorted by: [filename, namespace, name, kind, apiVersion]
|
||||
func (p TreeWriter) sort(indexByPackage map[string][]*yaml.RNode) []string {
|
||||
var keys []string
|
||||
for k := range indexByPackage {
|
||||
pkgNodes := indexByPackage[k]
|
||||
sort.Slice(pkgNodes, func(i, j int) bool { return compareNodes(pkgNodes[i], pkgNodes[j]) })
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
// return the package names sorted lexicographically
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func (p TreeWriter) doResource(leaf *yaml.RNode, metaString string, branch treeprint.Tree) (treeprint.Tree, error) {
|
||||
meta, _ := leaf.GetMeta()
|
||||
if metaString == "" {
|
||||
path := meta.Annotations[kioutil.PathAnnotation]
|
||||
path = filepath.Base(path)
|
||||
metaString = path
|
||||
}
|
||||
|
||||
value := fmt.Sprintf("%s %s", meta.Kind, meta.Name)
|
||||
if len(meta.Namespace) > 0 {
|
||||
value = fmt.Sprintf("%s %s/%s", meta.Kind, meta.Namespace, meta.Name)
|
||||
}
|
||||
|
||||
fields, err := p.getFields(leaf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n := branch.AddMetaBranch(metaString, value)
|
||||
for i := range fields {
|
||||
field := fields[i]
|
||||
|
||||
// do leaf node
|
||||
if len(field.matchingElementsAndFields) == 0 {
|
||||
n.AddNode(fmt.Sprintf("%s: %s", field.name, field.value))
|
||||
continue
|
||||
}
|
||||
|
||||
// do nested nodes
|
||||
b := n.AddBranch(field.name)
|
||||
for j := range field.matchingElementsAndFields {
|
||||
elem := field.matchingElementsAndFields[j]
|
||||
b := b.AddBranch(elem.name)
|
||||
for k := range elem.matchingElementsAndFields {
|
||||
field := elem.matchingElementsAndFields[k]
|
||||
b.AddNode(fmt.Sprintf("%s: %s", field.name, field.value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// getFields looks up p.Fields from leaf and structures them into treeFields.
|
||||
// TODO(pwittrock): simplify this function
|
||||
func (p TreeWriter) getFields(leaf *yaml.RNode) (treeFields, error) {
|
||||
fieldsByName := map[string]*treeField{}
|
||||
|
||||
// index nested and non-nested fields
|
||||
for i := range p.Fields {
|
||||
f := p.Fields[i]
|
||||
seq, err := leaf.Pipe(&f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if seq == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if fieldsByName[f.Name] == nil {
|
||||
fieldsByName[f.Name] = &treeField{name: f.Name}
|
||||
}
|
||||
|
||||
// non-nested field -- add directly to the treeFields list
|
||||
if f.SubName == "" {
|
||||
// non-nested field -- only 1 element
|
||||
val, err := yaml.String(seq.Content()[0], yaml.Trim, yaml.Flow)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldsByName[f.Name].value = val
|
||||
continue
|
||||
}
|
||||
|
||||
// nested-field -- create a parent elem, and index by the 'match' value
|
||||
if fieldsByName[f.Name].subFieldByMatch == nil {
|
||||
fieldsByName[f.Name].subFieldByMatch = map[string]treeFields{}
|
||||
}
|
||||
index := fieldsByName[f.Name].subFieldByMatch
|
||||
for j := range seq.Content() {
|
||||
elem := seq.Content()[j]
|
||||
matches := f.Matches[elem]
|
||||
str, err := yaml.String(elem, yaml.Trim, yaml.Flow)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// map the field by the name of the element
|
||||
// index the subfields by the matching element so we can put all the fields for the
|
||||
// same element under the same branch
|
||||
matchKey := strings.Join(matches, "/")
|
||||
index[matchKey] = append(index[matchKey], &treeField{name: f.SubName, value: str})
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over collection of all queried fields in the Resource
|
||||
for _, field := range fieldsByName {
|
||||
// iterate over collection of elements under the field -- indexed by element name
|
||||
for match, subFields := range field.subFieldByMatch {
|
||||
// create a new element for this collection of fields
|
||||
// note: we will convert name to an index later, but keep the match for sorting
|
||||
elem := &treeField{name: match}
|
||||
field.matchingElementsAndFields = append(field.matchingElementsAndFields, elem)
|
||||
|
||||
// iterate over collection of queried fields for the element
|
||||
for i := range subFields {
|
||||
// add to the list of fields for this element
|
||||
elem.matchingElementsAndFields = append(elem.matchingElementsAndFields, subFields[i])
|
||||
}
|
||||
}
|
||||
// clear this cached data
|
||||
field.subFieldByMatch = nil
|
||||
}
|
||||
|
||||
// put the fields in a list so they are ordered
|
||||
fieldList := treeFields{}
|
||||
for _, v := range fieldsByName {
|
||||
fieldList = append(fieldList, v)
|
||||
}
|
||||
|
||||
// sort the fields
|
||||
sort.Sort(fieldList)
|
||||
for i := range fieldList {
|
||||
field := fieldList[i]
|
||||
// sort the elements under this field
|
||||
sort.Sort(field.matchingElementsAndFields)
|
||||
|
||||
for i := range field.matchingElementsAndFields {
|
||||
element := field.matchingElementsAndFields[i]
|
||||
// sort the elements under a list field by their name
|
||||
sort.Sort(element.matchingElementsAndFields)
|
||||
// set the name of the element to its index
|
||||
element.name = fmt.Sprintf("%d", i)
|
||||
}
|
||||
}
|
||||
|
||||
return fieldList, nil
|
||||
}
|
||||
|
||||
// treeField wraps a field node
|
||||
type treeField struct {
|
||||
// name is the name of the node
|
||||
name string
|
||||
|
||||
// value is the value of the node -- may be empty
|
||||
value string
|
||||
|
||||
// matchingElementsAndFields is a slice of fields that go under this as a branch
|
||||
matchingElementsAndFields treeFields
|
||||
|
||||
// subFieldByMatch caches matchingElementsAndFields indexed by the name of the matching elem
|
||||
subFieldByMatch map[string]treeFields
|
||||
}
|
||||
|
||||
// treeFields wraps a slice of treeField so they can be sorted
|
||||
type treeFields []*treeField
|
||||
|
||||
func (nodes treeFields) Len() int { return len(nodes) }
|
||||
|
||||
func (nodes treeFields) Less(i, j int) bool {
|
||||
iIndex, iFound := yaml.FieldOrder[nodes[i].name]
|
||||
jIndex, jFound := yaml.FieldOrder[nodes[j].name]
|
||||
if iFound && jFound {
|
||||
return iIndex < jIndex
|
||||
}
|
||||
if iFound {
|
||||
return true
|
||||
}
|
||||
if jFound {
|
||||
return false
|
||||
}
|
||||
|
||||
if nodes[i].name != nodes[j].name {
|
||||
return nodes[i].name < nodes[j].name
|
||||
}
|
||||
if nodes[i].value != nodes[j].value {
|
||||
return nodes[i].value < nodes[j].value
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (nodes treeFields) Swap(i, j int) { nodes[i], nodes[j] = nodes[j], nodes[i] }
|
||||
521
vendor/sigs.k8s.io/kustomize/kyaml/runfn/runfn.go
generated
vendored
Normal file
521
vendor/sigs.k8s.io/kustomize/kyaml/runfn/runfn.go
generated
vendored
Normal file
@@ -0,0 +1,521 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package runfn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/container"
|
||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
|
||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// RunFns runs the set of configuration functions in a local directory against
|
||||
// the Resources in that directory
|
||||
type RunFns struct {
|
||||
StorageMounts []runtimeutil.StorageMount
|
||||
|
||||
// Path is the path to the directory containing functions
|
||||
Path string
|
||||
|
||||
// FunctionPaths Paths allows functions to be specified outside the configuration
|
||||
// directory.
|
||||
// Functions provided on FunctionPaths are globally scoped.
|
||||
// If FunctionPaths length is > 0, then NoFunctionsFromInput defaults to true
|
||||
FunctionPaths []string
|
||||
|
||||
// Functions is an explicit list of functions to run against the input.
|
||||
// Functions provided on Functions are globally scoped.
|
||||
// If Functions length is > 0, then NoFunctionsFromInput defaults to true
|
||||
Functions []*yaml.RNode
|
||||
|
||||
// GlobalScope if true, functions read from input will be scoped globally rather
|
||||
// than only to Resources under their subdirs.
|
||||
GlobalScope bool
|
||||
|
||||
// Input can be set to read the Resources from Input rather than from a directory
|
||||
Input io.Reader
|
||||
|
||||
// Network enables network access for functions that declare it
|
||||
Network bool
|
||||
|
||||
// Output can be set to write the result to Output rather than back to the directory
|
||||
Output io.Writer
|
||||
|
||||
// NoFunctionsFromInput if set to true will not read any functions from the input,
|
||||
// and only use explicit sources
|
||||
NoFunctionsFromInput *bool
|
||||
|
||||
// EnableStarlark will enable functions run as starlark scripts
|
||||
EnableStarlark bool
|
||||
|
||||
// EnableExec will enable exec functions
|
||||
EnableExec bool
|
||||
|
||||
// DisableContainers will disable functions run as containers
|
||||
DisableContainers bool
|
||||
|
||||
// ResultsDir is where to write each functions results
|
||||
ResultsDir string
|
||||
|
||||
// LogSteps enables logging the function that is running.
|
||||
LogSteps bool
|
||||
|
||||
// LogWriter can be set to write the logs to LogWriter rather than stderr if LogSteps is enabled.
|
||||
LogWriter io.Writer
|
||||
|
||||
// resultsCount is used to generate the results filename for each container
|
||||
resultsCount uint32
|
||||
|
||||
// functionFilterProvider provides a filter to perform the function.
|
||||
// this is a variable so it can be mocked in tests
|
||||
functionFilterProvider func(
|
||||
filter runtimeutil.FunctionSpec, api *yaml.RNode, currentUser currentUserFunc) (kio.Filter, error)
|
||||
|
||||
// AsCurrentUser is a boolean to indicate whether docker container should use
|
||||
// the uid and gid that run the command
|
||||
AsCurrentUser bool
|
||||
|
||||
// Env contains environment variables that will be exported to container
|
||||
Env []string
|
||||
|
||||
// ContinueOnEmptyResult configures what happens when the underlying pipeline
|
||||
// returns an empty result.
|
||||
// If it is false (default), subsequent functions will be skipped and the
|
||||
// result will be returned immediately.
|
||||
// If it is true, the empty result will be provided as input to the next
|
||||
// function in the list.
|
||||
ContinueOnEmptyResult bool
|
||||
}
|
||||
|
||||
// Execute runs the command
|
||||
func (r RunFns) Execute() error {
|
||||
// make the path absolute so it works on mac
|
||||
var err error
|
||||
r.Path, err = filepath.Abs(r.Path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// default the containerFilterProvider if it hasn't been override. Split out for testing.
|
||||
(&r).init()
|
||||
nodes, fltrs, output, err := r.getNodesAndFilters()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.runFunctions(nodes, output, fltrs)
|
||||
}
|
||||
|
||||
func (r RunFns) getNodesAndFilters() (
|
||||
*kio.PackageBuffer, []kio.Filter, *kio.LocalPackageReadWriter, error) {
|
||||
// Read Resources from Directory or Input
|
||||
buff := &kio.PackageBuffer{}
|
||||
p := kio.Pipeline{Outputs: []kio.Writer{buff}}
|
||||
// save the output dir because we will need it to write back
|
||||
// the same one for reading must be used for writing if deleting Resources
|
||||
var outputPkg *kio.LocalPackageReadWriter
|
||||
if r.Path != "" {
|
||||
outputPkg = &kio.LocalPackageReadWriter{PackagePath: r.Path, MatchFilesGlob: kio.MatchAll}
|
||||
}
|
||||
|
||||
if r.Input == nil {
|
||||
p.Inputs = []kio.Reader{outputPkg}
|
||||
} else {
|
||||
p.Inputs = []kio.Reader{&kio.ByteReader{Reader: r.Input}}
|
||||
}
|
||||
if err := p.Execute(); err != nil {
|
||||
return nil, nil, outputPkg, err
|
||||
}
|
||||
|
||||
fltrs, err := r.getFilters(buff.Nodes)
|
||||
if err != nil {
|
||||
return nil, nil, outputPkg, err
|
||||
}
|
||||
return buff, fltrs, outputPkg, nil
|
||||
}
|
||||
|
||||
func (r RunFns) getFilters(nodes []*yaml.RNode) ([]kio.Filter, error) {
|
||||
var fltrs []kio.Filter
|
||||
|
||||
// fns from annotations on the input resources
|
||||
f, err := r.getFunctionsFromInput(nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fltrs = append(fltrs, f...)
|
||||
|
||||
// fns from directories specified on the struct
|
||||
f, err = r.getFunctionsFromFunctionPaths()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fltrs = append(fltrs, f...)
|
||||
|
||||
// explicit fns specified on the struct
|
||||
f, err = r.getFunctionsFromFunctions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fltrs = append(fltrs, f...)
|
||||
|
||||
return fltrs, nil
|
||||
}
|
||||
|
||||
// runFunctions runs the fltrs against the input and writes to either r.Output or output
|
||||
func (r RunFns) runFunctions(
|
||||
input kio.Reader, output kio.Writer, fltrs []kio.Filter) error {
|
||||
// use the previously read Resources as input
|
||||
var outputs []kio.Writer
|
||||
if r.Output == nil {
|
||||
// write back to the package
|
||||
outputs = append(outputs, output)
|
||||
} else {
|
||||
// write to the output instead of the directory if r.Output is specified or
|
||||
// the output is nil (reading from Input)
|
||||
outputs = append(outputs, kio.ByteWriter{Writer: r.Output})
|
||||
}
|
||||
|
||||
var err error
|
||||
pipeline := kio.Pipeline{
|
||||
Inputs: []kio.Reader{input},
|
||||
Filters: fltrs,
|
||||
Outputs: outputs,
|
||||
ContinueOnEmptyResult: r.ContinueOnEmptyResult,
|
||||
}
|
||||
if r.LogSteps {
|
||||
err = pipeline.ExecuteWithCallback(func(op kio.Filter) {
|
||||
var identifier string
|
||||
|
||||
switch filter := op.(type) {
|
||||
case *container.Filter:
|
||||
identifier = filter.Image
|
||||
case *exec.Filter:
|
||||
identifier = filter.Path
|
||||
case *starlark.Filter:
|
||||
identifier = filter.String()
|
||||
default:
|
||||
identifier = "unknown-type function"
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(r.LogWriter, "Running %s\n", identifier)
|
||||
})
|
||||
} else {
|
||||
err = pipeline.Execute()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check for deferred function errors
|
||||
var errs []string
|
||||
for i := range fltrs {
|
||||
cf, ok := fltrs[i].(runtimeutil.DeferFailureFunction)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if cf.GetExit() != nil {
|
||||
errs = append(errs, cf.GetExit().Error())
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf(strings.Join(errs, "\n---\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getFunctionsFromInput scans the input for functions and runs them
|
||||
func (r RunFns) getFunctionsFromInput(nodes []*yaml.RNode) ([]kio.Filter, error) {
|
||||
if *r.NoFunctionsFromInput {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
buff := &kio.PackageBuffer{}
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.PackageBuffer{Nodes: nodes}},
|
||||
Filters: []kio.Filter{&runtimeutil.IsReconcilerFilter{}},
|
||||
Outputs: []kio.Writer{buff},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = sortFns(buff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.getFunctionFilters(false, buff.Nodes...)
|
||||
}
|
||||
|
||||
// getFunctionsFromFunctionPaths returns the set of functions read from r.FunctionPaths
|
||||
// as a slice of Filters
|
||||
func (r RunFns) getFunctionsFromFunctionPaths() ([]kio.Filter, error) {
|
||||
buff := &kio.PackageBuffer{}
|
||||
for i := range r.FunctionPaths {
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{
|
||||
kio.LocalPackageReader{PackagePath: r.FunctionPaths[i]},
|
||||
},
|
||||
Outputs: []kio.Writer{buff},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return r.getFunctionFilters(true, buff.Nodes...)
|
||||
}
|
||||
|
||||
// getFunctionsFromFunctions returns the set of explicitly provided functions as
|
||||
// Filters
|
||||
func (r RunFns) getFunctionsFromFunctions() ([]kio.Filter, error) {
|
||||
return r.getFunctionFilters(true, r.Functions...)
|
||||
}
|
||||
|
||||
// mergeContainerEnv will merge the envs specified by command line (imperative) and config
|
||||
// file (declarative). If they have same key, the imperative value will be respected.
|
||||
func (r RunFns) mergeContainerEnv(envs []string) []string {
|
||||
imperative := runtimeutil.NewContainerEnvFromStringSlice(r.Env)
|
||||
declarative := runtimeutil.NewContainerEnvFromStringSlice(envs)
|
||||
for key, value := range imperative.EnvVars {
|
||||
declarative.AddKeyValue(key, value)
|
||||
}
|
||||
|
||||
for _, key := range imperative.VarsToExport {
|
||||
declarative.AddKey(key)
|
||||
}
|
||||
|
||||
return declarative.Raw()
|
||||
}
|
||||
|
||||
func (r RunFns) getFunctionFilters(global bool, fns ...*yaml.RNode) (
|
||||
[]kio.Filter, error) {
|
||||
var fltrs []kio.Filter
|
||||
for i := range fns {
|
||||
api := fns[i]
|
||||
spec := runtimeutil.GetFunctionSpec(api)
|
||||
if spec == nil {
|
||||
// resource doesn't have function spec
|
||||
continue
|
||||
}
|
||||
if spec.Container.Network && !r.Network {
|
||||
// TODO(eddiezane): Provide error info about which function needs the network
|
||||
return fltrs, errors.Errorf("network required but not enabled with --network")
|
||||
}
|
||||
// merge envs from imperative and declarative
|
||||
spec.Container.Env = r.mergeContainerEnv(spec.Container.Env)
|
||||
|
||||
c, err := r.functionFilterProvider(*spec, api, user.Current)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c == nil {
|
||||
continue
|
||||
}
|
||||
cf, ok := c.(*container.Filter)
|
||||
if global && ok {
|
||||
cf.Exec.GlobalScope = true
|
||||
}
|
||||
fltrs = append(fltrs, c)
|
||||
}
|
||||
return fltrs, nil
|
||||
}
|
||||
|
||||
// sortFns sorts functions so that functions with the longest paths come first
|
||||
func sortFns(buff *kio.PackageBuffer) error {
|
||||
var outerErr error
|
||||
// sort the nodes so that we traverse them depth first
|
||||
// functions deeper in the file system tree should be run first
|
||||
sort.Slice(buff.Nodes, func(i, j int) bool {
|
||||
mi, _ := buff.Nodes[i].GetMeta()
|
||||
pi := filepath.ToSlash(mi.Annotations[kioutil.PathAnnotation])
|
||||
|
||||
mj, _ := buff.Nodes[j].GetMeta()
|
||||
pj := filepath.ToSlash(mj.Annotations[kioutil.PathAnnotation])
|
||||
|
||||
// If the path is the same, we decide the ordering based on the
|
||||
// index annotation.
|
||||
if pi == pj {
|
||||
iIndex, err := strconv.Atoi(mi.Annotations[kioutil.IndexAnnotation])
|
||||
if err != nil {
|
||||
outerErr = err
|
||||
return false
|
||||
}
|
||||
jIndex, err := strconv.Atoi(mj.Annotations[kioutil.IndexAnnotation])
|
||||
if err != nil {
|
||||
outerErr = err
|
||||
return false
|
||||
}
|
||||
return iIndex < jIndex
|
||||
}
|
||||
|
||||
if filepath.Base(path.Dir(pi)) == "functions" {
|
||||
// don't count the functions dir, the functions are scoped 1 level above
|
||||
pi = filepath.Dir(path.Dir(pi))
|
||||
} else {
|
||||
pi = filepath.Dir(pi)
|
||||
}
|
||||
|
||||
if filepath.Base(path.Dir(pj)) == "functions" {
|
||||
// don't count the functions dir, the functions are scoped 1 level above
|
||||
pj = filepath.Dir(path.Dir(pj))
|
||||
} else {
|
||||
pj = filepath.Dir(pj)
|
||||
}
|
||||
|
||||
// i is "less" than j (comes earlier) if its depth is greater -- e.g. run
|
||||
// i before j if it is deeper in the directory structure
|
||||
li := len(strings.Split(pi, "/"))
|
||||
if pi == "." {
|
||||
// local dir should have 0 path elements instead of 1
|
||||
li = 0
|
||||
}
|
||||
lj := len(strings.Split(pj, "/"))
|
||||
if pj == "." {
|
||||
// local dir should have 0 path elements instead of 1
|
||||
lj = 0
|
||||
}
|
||||
if li != lj {
|
||||
// use greater-than because we want to sort with the longest
|
||||
// paths FIRST rather than last
|
||||
return li > lj
|
||||
}
|
||||
|
||||
// sort by path names if depths are equal
|
||||
return pi < pj
|
||||
})
|
||||
return outerErr
|
||||
}
|
||||
|
||||
// init initializes the RunFns with a containerFilterProvider.
|
||||
func (r *RunFns) init() {
|
||||
if r.NoFunctionsFromInput == nil {
|
||||
// default no functions from input if any function sources are explicitly provided
|
||||
nfn := len(r.FunctionPaths) > 0 || len(r.Functions) > 0
|
||||
r.NoFunctionsFromInput = &nfn
|
||||
}
|
||||
|
||||
// if no path is specified, default reading from stdin and writing to stdout
|
||||
if r.Path == "" {
|
||||
if r.Output == nil {
|
||||
r.Output = os.Stdout
|
||||
}
|
||||
if r.Input == nil {
|
||||
r.Input = os.Stdin
|
||||
}
|
||||
}
|
||||
|
||||
// functionFilterProvider set the filter provider
|
||||
if r.functionFilterProvider == nil {
|
||||
r.functionFilterProvider = r.ffp
|
||||
}
|
||||
|
||||
// if LogSteps is enabled and LogWriter is not specified, use stderr
|
||||
if r.LogSteps && r.LogWriter == nil {
|
||||
r.LogWriter = os.Stderr
|
||||
}
|
||||
}
|
||||
|
||||
type currentUserFunc func() (*user.User, error)
|
||||
|
||||
// getUIDGID will return "nobody" if asCurrentUser is false. Otherwise
|
||||
// return "uid:gid" according to the return from currentUser function.
|
||||
func getUIDGID(asCurrentUser bool, currentUser currentUserFunc) (string, error) {
|
||||
if !asCurrentUser {
|
||||
return "nobody", nil
|
||||
}
|
||||
|
||||
u, err := currentUser()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%s:%s", u.Uid, u.Gid), nil
|
||||
}
|
||||
|
||||
// ffp provides function filters
|
||||
func (r *RunFns) ffp(spec runtimeutil.FunctionSpec, api *yaml.RNode, currentUser currentUserFunc) (kio.Filter, error) {
|
||||
var resultsFile string
|
||||
if r.ResultsDir != "" {
|
||||
resultsFile = filepath.Join(r.ResultsDir, fmt.Sprintf(
|
||||
"results-%v.yaml", r.resultsCount))
|
||||
atomic.AddUint32(&r.resultsCount, 1)
|
||||
}
|
||||
if !r.DisableContainers && spec.Container.Image != "" {
|
||||
// TODO: Add a test for this behavior
|
||||
uidgid, err := getUIDGID(r.AsCurrentUser, currentUser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := container.NewContainer(
|
||||
runtimeutil.ContainerSpec{
|
||||
Image: spec.Container.Image,
|
||||
Network: spec.Container.Network,
|
||||
StorageMounts: r.StorageMounts,
|
||||
Env: spec.Container.Env,
|
||||
},
|
||||
uidgid,
|
||||
)
|
||||
cf := &c
|
||||
cf.Exec.FunctionConfig = api
|
||||
cf.Exec.GlobalScope = r.GlobalScope
|
||||
cf.Exec.ResultsFile = resultsFile
|
||||
cf.Exec.DeferFailure = spec.DeferFailure
|
||||
return cf, nil
|
||||
}
|
||||
if r.EnableStarlark && (spec.Starlark.Path != "" || spec.Starlark.URL != "") {
|
||||
// the script path is relative to the function config file
|
||||
m, err := api.GetMeta()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
var p string
|
||||
if spec.Starlark.Path != "" {
|
||||
p = filepath.ToSlash(path.Clean(m.Annotations[kioutil.PathAnnotation]))
|
||||
spec.Starlark.Path = filepath.ToSlash(path.Clean(spec.Starlark.Path))
|
||||
if filepath.IsAbs(spec.Starlark.Path) || path.IsAbs(spec.Starlark.Path) {
|
||||
return nil, errors.Errorf(
|
||||
"absolute function path %s not allowed", spec.Starlark.Path)
|
||||
}
|
||||
if strings.HasPrefix(spec.Starlark.Path, "..") {
|
||||
return nil, errors.Errorf(
|
||||
"function path %s not allowed to start with ../", spec.Starlark.Path)
|
||||
}
|
||||
p = filepath.ToSlash(filepath.Join(r.Path, filepath.Dir(p), spec.Starlark.Path))
|
||||
}
|
||||
fmt.Println(p)
|
||||
|
||||
sf := &starlark.Filter{Name: spec.Starlark.Name, Path: p, URL: spec.Starlark.URL}
|
||||
|
||||
sf.FunctionConfig = api
|
||||
sf.GlobalScope = r.GlobalScope
|
||||
sf.ResultsFile = resultsFile
|
||||
sf.DeferFailure = spec.DeferFailure
|
||||
return sf, nil
|
||||
}
|
||||
|
||||
if r.EnableExec && spec.Exec.Path != "" {
|
||||
ef := &exec.Filter{Path: spec.Exec.Path}
|
||||
|
||||
ef.FunctionConfig = api
|
||||
ef.GlobalScope = r.GlobalScope
|
||||
ef.ResultsFile = resultsFile
|
||||
ef.DeferFailure = spec.DeferFailure
|
||||
return ef, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
182
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge2/merge2.go
generated
vendored
Normal file
182
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge2/merge2.go
generated
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package merge contains libraries for merging fields from one RNode to another
|
||||
// RNode
|
||||
package merge2
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml/walk"
|
||||
)
|
||||
|
||||
// Merge merges fields from src into dest.
|
||||
func Merge(src, dest *yaml.RNode, mergeOptions yaml.MergeOptions) (*yaml.RNode, error) {
|
||||
return walk.Walker{
|
||||
Sources: []*yaml.RNode{dest, src},
|
||||
Visitor: Merger{},
|
||||
MergeOptions: mergeOptions,
|
||||
}.Walk()
|
||||
}
|
||||
|
||||
// Merge parses the arguments, and merges fields from srcStr into destStr.
|
||||
func MergeStrings(srcStr, destStr string, infer bool, mergeOptions yaml.MergeOptions) (string, error) {
|
||||
src, err := yaml.Parse(srcStr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dest, err := yaml.Parse(destStr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result, err := walk.Walker{
|
||||
Sources: []*yaml.RNode{dest, src},
|
||||
Visitor: Merger{},
|
||||
InferAssociativeLists: infer,
|
||||
MergeOptions: mergeOptions,
|
||||
}.Walk()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return result.String()
|
||||
}
|
||||
|
||||
type Merger struct {
|
||||
// for forwards compatibility when new functions are added to the interface
|
||||
}
|
||||
|
||||
var _ walk.Visitor = Merger{}
|
||||
|
||||
func (m Merger) VisitMap(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
if err := m.SetComments(nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.SetStyle(nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if yaml.IsMissingOrNull(nodes.Dest()) {
|
||||
// Add
|
||||
ps, _ := determineSmpDirective(nodes.Origin())
|
||||
if ps == smpDelete {
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
|
||||
return nodes.Origin(), nil
|
||||
}
|
||||
if nodes.Origin().IsTaggedNull() {
|
||||
// clear the value
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
|
||||
ps, err := determineSmpDirective(nodes.Origin())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch ps {
|
||||
case smpDelete:
|
||||
return walk.ClearNode, nil
|
||||
case smpReplace:
|
||||
return nodes.Origin(), nil
|
||||
default:
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m Merger) VisitScalar(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
if err := m.SetComments(nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.SetStyle(nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Override value
|
||||
if nodes.Origin() != nil {
|
||||
return nodes.Origin(), nil
|
||||
}
|
||||
// Keep
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
func (m Merger) VisitList(nodes walk.Sources, s *openapi.ResourceSchema, kind walk.ListKind) (*yaml.RNode, error) {
|
||||
if err := m.SetComments(nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.SetStyle(nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if kind == walk.NonAssociateList {
|
||||
// Override value
|
||||
if nodes.Origin() != nil {
|
||||
return nodes.Origin(), nil
|
||||
}
|
||||
// Keep
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
// Add
|
||||
if yaml.IsMissingOrNull(nodes.Dest()) {
|
||||
return nodes.Origin(), nil
|
||||
}
|
||||
// Clear
|
||||
if nodes.Origin().IsTaggedNull() {
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
|
||||
ps, err := determineSmpDirective(nodes.Origin())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch ps {
|
||||
case smpDelete:
|
||||
return walk.ClearNode, nil
|
||||
case smpReplace:
|
||||
return nodes.Origin(), nil
|
||||
default:
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m Merger) SetStyle(sources walk.Sources) error {
|
||||
source := sources.Origin()
|
||||
dest := sources.Dest()
|
||||
if dest == nil || dest.YNode() == nil || source == nil || source.YNode() == nil {
|
||||
// avoid panic
|
||||
return nil
|
||||
}
|
||||
|
||||
// copy the style from the source.
|
||||
// special case: if the dest was an empty map or seq, then it probably had
|
||||
// folded style applied, but we actually want to keep the style of the origin
|
||||
// in this case (even if it was the default). otherwise the merged elements
|
||||
// will get folded even though this probably isn't what is desired.
|
||||
if dest.YNode().Kind != yaml.ScalarNode && len(dest.YNode().Content) == 0 {
|
||||
dest.YNode().Style = source.YNode().Style
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetComments copies the dest comments to the source comments if they are present
|
||||
// on the source.
|
||||
func (m Merger) SetComments(sources walk.Sources) error {
|
||||
source := sources.Origin()
|
||||
dest := sources.Dest()
|
||||
if dest == nil || dest.YNode() == nil || source == nil || source.YNode() == nil {
|
||||
// avoid panic
|
||||
return nil
|
||||
}
|
||||
if source.YNode().FootComment != "" {
|
||||
dest.YNode().FootComment = source.YNode().FootComment
|
||||
}
|
||||
if source.YNode().HeadComment != "" {
|
||||
dest.YNode().HeadComment = source.YNode().HeadComment
|
||||
}
|
||||
if source.YNode().LineComment != "" {
|
||||
dest.YNode().LineComment = source.YNode().LineComment
|
||||
}
|
||||
return nil
|
||||
}
|
||||
101
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge2/smpdirective.go
generated
vendored
Normal file
101
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge2/smpdirective.go
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package merge2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// A strategic merge patch directive.
|
||||
// See https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md
|
||||
//
|
||||
//go:generate stringer -type=smpDirective -linecomment
|
||||
type smpDirective int
|
||||
|
||||
const (
|
||||
smpUnknown smpDirective = iota // unknown
|
||||
smpReplace // replace
|
||||
smpDelete // delete
|
||||
smpMerge // merge
|
||||
)
|
||||
|
||||
const strategicMergePatchDirectiveKey = "$patch"
|
||||
|
||||
// Examine patch for a strategic merge patch directive.
|
||||
// If found, return it, and remove the directive from the patch.
|
||||
func determineSmpDirective(patch *yaml.RNode) (smpDirective, error) {
|
||||
if patch == nil {
|
||||
return smpMerge, nil
|
||||
}
|
||||
switch patch.YNode().Kind {
|
||||
case yaml.SequenceNode:
|
||||
return determineSequenceNodePatchStrategy(patch)
|
||||
case yaml.MappingNode:
|
||||
return determineMappingNodePatchStrategy(patch)
|
||||
default:
|
||||
return smpUnknown, fmt.Errorf(
|
||||
"no implemented strategic merge patch strategy for '%s' ('%s')",
|
||||
patch.YNode().ShortTag(), patch.MustString())
|
||||
}
|
||||
}
|
||||
|
||||
func determineSequenceNodePatchStrategy(patch *yaml.RNode) (smpDirective, error) {
|
||||
// get the $patch element
|
||||
node, err := patch.Pipe(yaml.GetElementByKey(strategicMergePatchDirectiveKey))
|
||||
// if there are more than 1 key/value pair in the map, then this $patch
|
||||
// is not for the sequence
|
||||
if err != nil || node == nil || node.YNode() == nil || len(node.Content()) > 2 {
|
||||
return smpMerge, nil
|
||||
}
|
||||
// get the value
|
||||
value, err := node.Pipe(yaml.Get(strategicMergePatchDirectiveKey))
|
||||
if err != nil || value == nil || value.YNode() == nil {
|
||||
return smpMerge, nil
|
||||
}
|
||||
v := value.YNode().Value
|
||||
if v == smpDelete.String() {
|
||||
return smpDelete, elideSequencePatchDirective(patch, v)
|
||||
}
|
||||
if v == smpReplace.String() {
|
||||
return smpReplace, elideSequencePatchDirective(patch, v)
|
||||
}
|
||||
if v == smpMerge.String() {
|
||||
return smpMerge, elideSequencePatchDirective(patch, v)
|
||||
}
|
||||
return smpUnknown, fmt.Errorf(
|
||||
"unknown patch strategy '%s'", v)
|
||||
}
|
||||
|
||||
func determineMappingNodePatchStrategy(patch *yaml.RNode) (smpDirective, error) {
|
||||
node, err := patch.Pipe(yaml.Get(strategicMergePatchDirectiveKey))
|
||||
if err != nil || node == nil || node.YNode() == nil {
|
||||
return smpMerge, nil
|
||||
}
|
||||
v := node.YNode().Value
|
||||
if v == smpDelete.String() {
|
||||
return smpDelete, elideMappingPatchDirective(patch)
|
||||
}
|
||||
if v == smpReplace.String() {
|
||||
return smpReplace, elideMappingPatchDirective(patch)
|
||||
}
|
||||
if v == smpMerge.String() {
|
||||
return smpMerge, elideMappingPatchDirective(patch)
|
||||
}
|
||||
return smpUnknown, fmt.Errorf(
|
||||
"unknown patch strategy '%s'", v)
|
||||
}
|
||||
|
||||
func elideMappingPatchDirective(patch *yaml.RNode) error {
|
||||
return patch.PipeE(yaml.Clear(strategicMergePatchDirectiveKey))
|
||||
}
|
||||
|
||||
func elideSequencePatchDirective(patch *yaml.RNode, value string) error {
|
||||
return patch.PipeE(yaml.ElementSetter{
|
||||
Element: nil,
|
||||
Keys: []string{strategicMergePatchDirectiveKey},
|
||||
Values: []string{value},
|
||||
})
|
||||
}
|
||||
26
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge2/smpdirective_string.go
generated
vendored
Normal file
26
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge2/smpdirective_string.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// Code generated by "stringer -type=smpDirective -linecomment"; DO NOT EDIT.
|
||||
|
||||
package merge2
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[smpUnknown-0]
|
||||
_ = x[smpReplace-1]
|
||||
_ = x[smpDelete-2]
|
||||
_ = x[smpMerge-3]
|
||||
}
|
||||
|
||||
const _smpDirective_name = "unknownreplacedeletemerge"
|
||||
|
||||
var _smpDirective_index = [...]uint8{0, 7, 14, 20, 25}
|
||||
|
||||
func (i smpDirective) String() string {
|
||||
if i < 0 || i >= smpDirective(len(_smpDirective_index)-1) {
|
||||
return "smpDirective(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _smpDirective_name[_smpDirective_index[i]:_smpDirective_index[i+1]]
|
||||
}
|
||||
45
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge3/merge3.go
generated
vendored
Normal file
45
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge3/merge3.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package merge contains libraries for merging fields from one RNode to another
|
||||
// RNode
|
||||
package merge3
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml/walk"
|
||||
)
|
||||
|
||||
func Merge(dest, original, update *yaml.RNode) (*yaml.RNode, error) {
|
||||
// if update == nil && original != nil => declarative deletion
|
||||
|
||||
return walk.Walker{
|
||||
Visitor: Visitor{},
|
||||
VisitKeysAsScalars: true,
|
||||
Sources: []*yaml.RNode{dest, original, update}}.Walk()
|
||||
}
|
||||
|
||||
func MergeStrings(dest, original, update string, infer bool) (string, error) {
|
||||
srcOriginal, err := yaml.Parse(original)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
srcUpdated, err := yaml.Parse(update)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
d, err := yaml.Parse(dest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result, err := walk.Walker{
|
||||
InferAssociativeLists: infer,
|
||||
Visitor: Visitor{},
|
||||
VisitKeysAsScalars: true,
|
||||
Sources: []*yaml.RNode{d, srcOriginal, srcUpdated}}.Walk()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
172
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge3/visitor.go
generated
vendored
Normal file
172
vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge3/visitor.go
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package merge3
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml/walk"
|
||||
)
|
||||
|
||||
type ConflictStrategy uint
|
||||
|
||||
const (
|
||||
// TODO: Support more strategies
|
||||
TakeUpdate ConflictStrategy = 1 + iota
|
||||
)
|
||||
|
||||
type Visitor struct{}
|
||||
|
||||
func (m Visitor) VisitMap(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
if nodes.Updated().IsTaggedNull() || nodes.Dest().IsTaggedNull() {
|
||||
// explicitly cleared from either dest or update
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
if nodes.Dest() == nil && nodes.Updated() == nil {
|
||||
// implicitly cleared missing from both dest and update
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
|
||||
if nodes.Dest() == nil {
|
||||
// not cleared, but missing from the dest
|
||||
// initialize a new value that can be recursively merged
|
||||
return yaml.NewRNode(&yaml.Node{Kind: yaml.MappingNode}), nil
|
||||
}
|
||||
|
||||
// recursively merge the dest with the original and updated
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
func (m Visitor) visitAList(nodes walk.Sources, _ *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
if yaml.IsMissingOrNull(nodes.Updated()) && !yaml.IsMissingOrNull(nodes.Origin()) {
|
||||
// implicitly cleared from update -- element was deleted
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
if yaml.IsMissingOrNull(nodes.Dest()) {
|
||||
// not cleared, but missing from the dest
|
||||
// initialize a new value that can be recursively merged
|
||||
return yaml.NewRNode(&yaml.Node{Kind: yaml.SequenceNode}), nil
|
||||
}
|
||||
|
||||
// recursively merge the dest with the original and updated
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
func (m Visitor) VisitScalar(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
|
||||
if nodes.Updated().IsTaggedNull() || nodes.Dest().IsTaggedNull() {
|
||||
// explicitly cleared from either dest or update
|
||||
return nil, nil
|
||||
}
|
||||
if yaml.IsMissingOrNull(nodes.Updated()) != yaml.IsMissingOrNull(nodes.Origin()) {
|
||||
// value added or removed in update
|
||||
return nodes.Updated(), nil
|
||||
}
|
||||
if yaml.IsMissingOrNull(nodes.Updated()) && yaml.IsMissingOrNull(nodes.Origin()) {
|
||||
// value added or removed in update
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
values, err := m.getStrValues(nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if (values.Dest == "" || values.Dest == values.Origin) && values.Origin != values.Update {
|
||||
// if local is nil or is unchanged but there is new update
|
||||
return nodes.Updated(), nil
|
||||
}
|
||||
|
||||
if nodes.Updated().YNode().Value != nodes.Origin().YNode().Value {
|
||||
// value changed in update
|
||||
return nodes.Updated(), nil
|
||||
}
|
||||
|
||||
// unchanged between origin and update, keep the dest
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
func (m Visitor) visitNAList(nodes walk.Sources) (*yaml.RNode, error) {
|
||||
if nodes.Updated().IsTaggedNull() || nodes.Dest().IsTaggedNull() {
|
||||
// explicitly cleared from either dest or update
|
||||
return walk.ClearNode, nil
|
||||
}
|
||||
|
||||
if yaml.IsMissingOrNull(nodes.Updated()) != yaml.IsMissingOrNull(nodes.Origin()) {
|
||||
// value added or removed in update
|
||||
return nodes.Updated(), nil
|
||||
}
|
||||
if yaml.IsMissingOrNull(nodes.Updated()) && yaml.IsMissingOrNull(nodes.Origin()) {
|
||||
// value not present in source or dest
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
// compare origin and update values to see if they have changed
|
||||
values, err := m.getStrValues(nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if values.Update != values.Origin {
|
||||
// value changed in update
|
||||
return nodes.Updated(), nil
|
||||
}
|
||||
|
||||
// unchanged between origin and update, keep the dest
|
||||
return nodes.Dest(), nil
|
||||
}
|
||||
|
||||
func (m Visitor) VisitList(nodes walk.Sources, s *openapi.ResourceSchema, kind walk.ListKind) (*yaml.RNode, error) {
|
||||
if kind == walk.AssociativeList {
|
||||
return m.visitAList(nodes, s)
|
||||
}
|
||||
// non-associative list
|
||||
return m.visitNAList(nodes)
|
||||
}
|
||||
|
||||
func (m Visitor) getStrValues(nodes walk.Sources) (strValues, error) {
|
||||
var uStr, oStr, dStr string
|
||||
var err error
|
||||
if nodes.Updated() != nil && nodes.Updated().YNode() != nil {
|
||||
s := nodes.Updated().YNode().Style
|
||||
defer func() {
|
||||
nodes.Updated().YNode().Style = s
|
||||
}()
|
||||
nodes.Updated().YNode().Style = yaml.FlowStyle | yaml.SingleQuotedStyle
|
||||
uStr, err = nodes.Updated().String()
|
||||
if err != nil {
|
||||
return strValues{}, err
|
||||
}
|
||||
}
|
||||
if nodes.Origin() != nil && nodes.Origin().YNode() != nil {
|
||||
s := nodes.Origin().YNode().Style
|
||||
defer func() {
|
||||
nodes.Origin().YNode().Style = s
|
||||
}()
|
||||
nodes.Origin().YNode().Style = yaml.FlowStyle | yaml.SingleQuotedStyle
|
||||
oStr, err = nodes.Origin().String()
|
||||
if err != nil {
|
||||
return strValues{}, err
|
||||
}
|
||||
}
|
||||
if nodes.Dest() != nil && nodes.Dest().YNode() != nil {
|
||||
s := nodes.Dest().YNode().Style
|
||||
defer func() {
|
||||
nodes.Dest().YNode().Style = s
|
||||
}()
|
||||
nodes.Dest().YNode().Style = yaml.FlowStyle | yaml.SingleQuotedStyle
|
||||
dStr, err = nodes.Dest().String()
|
||||
if err != nil {
|
||||
return strValues{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return strValues{Origin: oStr, Update: uStr, Dest: dStr}, nil
|
||||
}
|
||||
|
||||
type strValues struct {
|
||||
Origin string
|
||||
Update string
|
||||
Dest string
|
||||
}
|
||||
|
||||
var _ walk.Visitor = Visitor{}
|
||||
44
vendor/sigs.k8s.io/kustomize/kyaml/yaml/schema/schema.go
generated
vendored
Normal file
44
vendor/sigs.k8s.io/kustomize/kyaml/yaml/schema/schema.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package schema contains libraries for working with the yaml and openapi packages.
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// IsAssociative returns true if all elements in the list contain an
|
||||
// AssociativeSequenceKey as a field.
|
||||
func IsAssociative(schema *openapi.ResourceSchema, nodes []*yaml.RNode, infer bool) bool {
|
||||
if schema != nil {
|
||||
return schemaHasMergeStrategy(schema)
|
||||
}
|
||||
if !infer {
|
||||
return false
|
||||
}
|
||||
for i := range nodes {
|
||||
node := nodes[i]
|
||||
if yaml.IsMissingOrNull(node) {
|
||||
continue
|
||||
}
|
||||
if node.IsAssociative() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func schemaHasMergeStrategy(schema *openapi.ResourceSchema) bool {
|
||||
tmp, _ := schema.PatchStrategyAndKey()
|
||||
strategies := strings.Split(tmp, ",")
|
||||
for _, s := range strategies {
|
||||
if s == "merge" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
385
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/associative_sequence.go
generated
vendored
Normal file
385
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/associative_sequence.go
generated
vendored
Normal file
@@ -0,0 +1,385 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package walk
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/sets"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// appendListNode will append the nodes from src to dst and return dst.
|
||||
// src and dst should be both sequence node. key is used to call ElementSetter.
|
||||
// ElementSetter will use key-value pair to find and set the element in sequence
|
||||
// node.
|
||||
func appendListNode(dst, src *yaml.RNode, keys []string) (*yaml.RNode, error) {
|
||||
var err error
|
||||
for _, elem := range src.Content() {
|
||||
// If key is empty, we know this is a scalar value and we can directly set the
|
||||
// node
|
||||
if keys[0] == "" {
|
||||
_, err = dst.Pipe(yaml.ElementSetter{
|
||||
Element: elem,
|
||||
Keys: []string{""},
|
||||
Values: []string{elem.Value},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// we need to get the value for key so that we can find the element to set
|
||||
// in sequence.
|
||||
v := []string{}
|
||||
for _, key := range keys {
|
||||
tmpNode := yaml.NewRNode(elem)
|
||||
valueNode, err := tmpNode.Pipe(yaml.Get(key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if valueNode.IsNil() {
|
||||
// no key found, directly append to dst
|
||||
err = dst.PipeE(yaml.Append(elem))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
v = append(v, valueNode.YNode().Value)
|
||||
}
|
||||
|
||||
// When there are multiple keys, ElementSetter appends the node to dst
|
||||
// even if the output is already in dst. We remove the node from dst to
|
||||
// prevent duplicates.
|
||||
if len(keys) > 1 {
|
||||
_, err = dst.Pipe(yaml.ElementSetter{
|
||||
Keys: keys,
|
||||
Values: v,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// We use the key and value from elem to find the corresponding element in dst.
|
||||
// Then we will use ElementSetter to replace the element with elem. If we cannot
|
||||
// find the item, the element will be appended.
|
||||
_, err = dst.Pipe(yaml.ElementSetter{
|
||||
Element: elem,
|
||||
Keys: keys,
|
||||
Values: v,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// validateKeys returns a list of valid key-value pairs
|
||||
// if secondary merge key values are missing, use only the available merge keys
|
||||
func validateKeys(valuesList [][]string, values []string, keys []string) ([]string, []string) {
|
||||
validKeys := make([]string, 0)
|
||||
validValues := make([]string, 0)
|
||||
validKeySet := sets.String{}
|
||||
for _, values := range valuesList {
|
||||
for i, v := range values {
|
||||
if v != "" {
|
||||
validKeySet.Insert(keys[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
if validKeySet.Len() == 0 { // if values missing, fall back to primary keys
|
||||
return keys, values
|
||||
}
|
||||
for _, k := range keys {
|
||||
if validKeySet.Has(k) {
|
||||
validKeys = append(validKeys, k)
|
||||
}
|
||||
}
|
||||
for i, v := range values {
|
||||
if v != "" || validKeySet.Has(keys[i]) {
|
||||
validValues = append(validValues, v)
|
||||
}
|
||||
}
|
||||
return validKeys, validValues
|
||||
}
|
||||
|
||||
// mergeValues merges values together - e.g. if two containerPorts
|
||||
// have the same port and targetPort but one has an empty protocol
|
||||
// and the other doesn't, they are treated as the same containerPort
|
||||
func mergeValues(valuesList [][]string) [][]string {
|
||||
for i, values1 := range valuesList {
|
||||
for j, values2 := range valuesList {
|
||||
if matched, values := match(values1, values2); matched {
|
||||
valuesList[i] = values
|
||||
valuesList[j] = values
|
||||
}
|
||||
}
|
||||
}
|
||||
return valuesList
|
||||
}
|
||||
|
||||
// two values match if they have at least one common element and
|
||||
// corresponding elements only differ if one is an empty string
|
||||
func match(values1 []string, values2 []string) (bool, []string) {
|
||||
if len(values1) != len(values2) {
|
||||
return false, nil
|
||||
}
|
||||
var commonElement bool
|
||||
var res []string
|
||||
for i := range values1 {
|
||||
if values1[i] == values2[i] {
|
||||
commonElement = true
|
||||
res = append(res, values1[i])
|
||||
continue
|
||||
}
|
||||
if values1[i] != "" && values2[i] != "" {
|
||||
return false, nil
|
||||
}
|
||||
if values1[i] != "" {
|
||||
res = append(res, values1[i])
|
||||
} else {
|
||||
res = append(res, values2[i])
|
||||
}
|
||||
}
|
||||
return commonElement, res
|
||||
}
|
||||
|
||||
// setAssociativeSequenceElements recursively set the elements in the list
|
||||
func (l *Walker) setAssociativeSequenceElements(valuesList [][]string, keys []string, dest *yaml.RNode) (*yaml.RNode, error) {
|
||||
// itemsToBeAdded contains the items that will be added to dest
|
||||
itemsToBeAdded := yaml.NewListRNode()
|
||||
var schema *openapi.ResourceSchema
|
||||
if l.Schema != nil {
|
||||
schema = l.Schema.Elements()
|
||||
}
|
||||
if len(keys) > 1 {
|
||||
valuesList = mergeValues(valuesList)
|
||||
}
|
||||
|
||||
// each element in valuesList is a list of values corresponding to the keys
|
||||
// for example, for the following yaml:
|
||||
// - containerPort: 8080
|
||||
// protocol: UDP
|
||||
// - containerPort: 8080
|
||||
// protocol: TCP
|
||||
// `keys` would be [containerPort, protocol]
|
||||
// and `valuesList` would be [ [8080, UDP], [8080, TCP] ]
|
||||
var validKeys []string
|
||||
var validValues []string
|
||||
for _, values := range valuesList {
|
||||
if len(values) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
validKeys, validValues = validateKeys(valuesList, values, keys)
|
||||
val, err := Walker{
|
||||
VisitKeysAsScalars: l.VisitKeysAsScalars,
|
||||
InferAssociativeLists: l.InferAssociativeLists,
|
||||
Visitor: l,
|
||||
Schema: schema,
|
||||
Sources: l.elementValueList(validKeys, validValues),
|
||||
MergeOptions: l.MergeOptions,
|
||||
}.Walk()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
exit := false
|
||||
for i, key := range validKeys {
|
||||
// delete the node from **dest** if it's null or empty
|
||||
if yaml.IsMissingOrNull(val) || yaml.IsEmptyMap(val) {
|
||||
_, err = dest.Pipe(yaml.ElementSetter{
|
||||
Keys: validKeys,
|
||||
Values: validValues,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exit = true
|
||||
} else if val.Field(key) == nil && validValues[i] != "" {
|
||||
// make sure the key is set on the field
|
||||
_, err = val.Pipe(yaml.SetField(key, yaml.NewScalarRNode(validValues[i])))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if exit {
|
||||
continue
|
||||
}
|
||||
|
||||
// Add the val to the sequence. val will replace the item in the sequence if
|
||||
// there is an item that matches all key-value pairs. Otherwise val will be appended
|
||||
// the the sequence.
|
||||
_, err = itemsToBeAdded.Pipe(yaml.ElementSetter{
|
||||
Element: val.YNode(),
|
||||
Keys: validKeys,
|
||||
Values: validValues,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
if len(valuesList) > 0 {
|
||||
if l.MergeOptions.ListIncreaseDirection == yaml.MergeOptionsListPrepend {
|
||||
// items from patches are needed to be prepended. so we append the
|
||||
// dest to itemsToBeAdded
|
||||
dest, err = appendListNode(itemsToBeAdded, dest, validKeys)
|
||||
} else {
|
||||
// append the items
|
||||
dest, err = appendListNode(dest, itemsToBeAdded, validKeys)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// sequence is empty
|
||||
if yaml.IsMissingOrNull(dest) {
|
||||
return nil, nil
|
||||
}
|
||||
return dest, nil
|
||||
}
|
||||
|
||||
func (l *Walker) walkAssociativeSequence() (*yaml.RNode, error) {
|
||||
// may require initializing the dest node
|
||||
dest, err := l.Sources.setDestNode(l.VisitList(l.Sources, l.Schema, AssociativeList))
|
||||
if dest == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// get the merge key(s) from schema
|
||||
var strategy string
|
||||
var keys []string
|
||||
if l.Schema != nil {
|
||||
strategy, keys = l.Schema.PatchStrategyAndKeyList()
|
||||
}
|
||||
if strategy == "" && len(keys) == 0 { // neither strategy nor keys present in the schema -- infer the key
|
||||
// find the list of elements we need to recursively walk
|
||||
key, err := l.elementKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if key != "" {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
|
||||
// non-primitive associative list -- merge the elements
|
||||
values := l.elementValues(keys)
|
||||
if len(values) != 0 || len(keys) > 0 {
|
||||
return l.setAssociativeSequenceElements(values, keys, dest)
|
||||
}
|
||||
|
||||
// primitive associative list -- merge the values
|
||||
return l.setAssociativeSequenceElements(l.elementPrimitiveValues(), []string{""}, dest)
|
||||
}
|
||||
|
||||
// elementKey returns the merge key to use for the associative list
|
||||
func (l Walker) elementKey() (string, error) {
|
||||
var key string
|
||||
for i := range l.Sources {
|
||||
if l.Sources[i] != nil && len(l.Sources[i].Content()) > 0 {
|
||||
newKey := l.Sources[i].GetAssociativeKey()
|
||||
if key != "" && key != newKey {
|
||||
return "", errors.Errorf(
|
||||
"conflicting merge keys [%s,%s] for field %s",
|
||||
key, newKey, strings.Join(l.Path, "."))
|
||||
}
|
||||
key = newKey
|
||||
}
|
||||
}
|
||||
if key == "" {
|
||||
return "", errors.Errorf("no merge key found for field %s",
|
||||
strings.Join(l.Path, "."))
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// elementValues returns a slice containing all values for the field across all elements
|
||||
// from all sources.
|
||||
// Return value slice is ordered using the original ordering from the elements, where
|
||||
// elements missing from earlier sources appear later.
|
||||
func (l Walker) elementValues(keys []string) [][]string {
|
||||
// use slice to to keep elements in the original order
|
||||
var returnValues [][]string
|
||||
var seen sets.StringList
|
||||
|
||||
// if we are doing append, dest node should be the first.
|
||||
// otherwise dest node should be the last.
|
||||
beginIdx := 0
|
||||
if l.MergeOptions.ListIncreaseDirection == yaml.MergeOptionsListPrepend {
|
||||
beginIdx = 1
|
||||
}
|
||||
for i := range l.Sources {
|
||||
src := l.Sources[(i+beginIdx)%len(l.Sources)]
|
||||
if src == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// add the value of the field for each element
|
||||
// don't check error, we know this is a list node
|
||||
values, _ := src.ElementValuesList(keys)
|
||||
for _, s := range values {
|
||||
if len(s) == 0 || seen.Has(s) {
|
||||
continue
|
||||
}
|
||||
returnValues = append(returnValues, s)
|
||||
seen = seen.Insert(s)
|
||||
}
|
||||
}
|
||||
return returnValues
|
||||
}
|
||||
|
||||
// elementPrimitiveValues returns the primitive values in an associative list -- eg. finalizers
|
||||
func (l Walker) elementPrimitiveValues() [][]string {
|
||||
// use slice to to keep elements in the original order
|
||||
var returnValues [][]string
|
||||
seen := sets.String{}
|
||||
// if we are doing append, dest node should be the first.
|
||||
// otherwise dest node should be the last.
|
||||
beginIdx := 0
|
||||
if l.MergeOptions.ListIncreaseDirection == yaml.MergeOptionsListPrepend {
|
||||
beginIdx = 1
|
||||
}
|
||||
for i := range l.Sources {
|
||||
src := l.Sources[(i+beginIdx)%len(l.Sources)]
|
||||
if src == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// add the value of the field for each element
|
||||
// don't check error, we know this is a list node
|
||||
for _, item := range src.YNode().Content {
|
||||
if seen.Has(item.Value) {
|
||||
continue
|
||||
}
|
||||
returnValues = append(returnValues, []string{item.Value})
|
||||
seen.Insert(item.Value)
|
||||
}
|
||||
}
|
||||
return returnValues
|
||||
}
|
||||
|
||||
// fieldValue returns a slice containing each source's value for fieldName
|
||||
func (l Walker) elementValueList(keys []string, values []string) []*yaml.RNode {
|
||||
keys, values = validateKeys([][]string{values}, values, keys)
|
||||
var fields []*yaml.RNode
|
||||
for i := range l.Sources {
|
||||
if l.Sources[i] == nil {
|
||||
fields = append(fields, nil)
|
||||
continue
|
||||
}
|
||||
fields = append(fields, l.Sources[i].ElementList(keys, values))
|
||||
}
|
||||
return fields
|
||||
}
|
||||
173
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/map.go
generated
vendored
Normal file
173
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/map.go
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package walk
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/sets"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// walkMap returns the value of VisitMap
|
||||
//
|
||||
// - call VisitMap
|
||||
// - set the return value on l.Dest
|
||||
// - walk each source field
|
||||
// - set each source field value on l.Dest
|
||||
func (l Walker) walkMap() (*yaml.RNode, error) {
|
||||
// get the new map value
|
||||
dest, err := l.Sources.setDestNode(l.VisitMap(l.Sources, l.Schema))
|
||||
if dest == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// recursively set the field values on the map
|
||||
for _, key := range l.fieldNames() {
|
||||
var res *yaml.RNode
|
||||
var keys []*yaml.RNode
|
||||
if l.VisitKeysAsScalars {
|
||||
// visit the map keys as if they were scalars,
|
||||
// this is necessary if doing things such as copying
|
||||
// comments
|
||||
for i := range l.Sources {
|
||||
// construct the sources from the keys
|
||||
if l.Sources[i] == nil {
|
||||
keys = append(keys, nil)
|
||||
continue
|
||||
}
|
||||
field := l.Sources[i].Field(key)
|
||||
if field == nil || yaml.IsMissingOrNull(field.Key) {
|
||||
keys = append(keys, nil)
|
||||
continue
|
||||
}
|
||||
keys = append(keys, field.Key)
|
||||
}
|
||||
// visit the sources as a scalar
|
||||
// keys don't have any schema --pass in nil
|
||||
res, err = l.Visitor.VisitScalar(keys, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var s *openapi.ResourceSchema
|
||||
if l.Schema != nil {
|
||||
s = l.Schema.Field(key)
|
||||
}
|
||||
fv, commentSch := l.fieldValue(key)
|
||||
if commentSch != nil {
|
||||
s = commentSch
|
||||
}
|
||||
val, err := Walker{
|
||||
VisitKeysAsScalars: l.VisitKeysAsScalars,
|
||||
InferAssociativeLists: l.InferAssociativeLists,
|
||||
Visitor: l,
|
||||
Schema: s,
|
||||
Sources: fv,
|
||||
MergeOptions: l.MergeOptions,
|
||||
Path: append(l.Path, key)}.Walk()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// transfer the comments of res to dest node
|
||||
var comments yaml.Comments
|
||||
if !yaml.IsMissingOrNull(res) {
|
||||
comments = yaml.Comments{
|
||||
LineComment: res.YNode().LineComment,
|
||||
HeadComment: res.YNode().HeadComment,
|
||||
FootComment: res.YNode().FootComment,
|
||||
}
|
||||
if len(keys) > 0 && !yaml.IsMissingOrNull(keys[DestIndex]) {
|
||||
keys[DestIndex].YNode().HeadComment = res.YNode().HeadComment
|
||||
keys[DestIndex].YNode().LineComment = res.YNode().LineComment
|
||||
keys[DestIndex].YNode().FootComment = res.YNode().FootComment
|
||||
}
|
||||
}
|
||||
|
||||
// this handles empty and non-empty values
|
||||
_, err = dest.Pipe(yaml.FieldSetter{Name: key, Comments: comments, Value: val})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return dest, nil
|
||||
}
|
||||
|
||||
// valueIfPresent returns node.Value if node is non-nil, otherwise returns nil
|
||||
func (l Walker) valueIfPresent(node *yaml.MapNode) (*yaml.RNode, *openapi.ResourceSchema) {
|
||||
if node == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// parse the schema for the field if present
|
||||
var s *openapi.ResourceSchema
|
||||
fm := fieldmeta.FieldMeta{}
|
||||
var err error
|
||||
// check the value for a schema
|
||||
if err = fm.Read(node.Value); err == nil {
|
||||
s = &openapi.ResourceSchema{Schema: &fm.Schema}
|
||||
if fm.Schema.Ref.String() != "" {
|
||||
r, err := openapi.Resolve(&fm.Schema.Ref, openapi.Schema())
|
||||
if err == nil && r != nil {
|
||||
s.Schema = r
|
||||
}
|
||||
}
|
||||
}
|
||||
// check the key for a schema -- this will be used
|
||||
// when the value is a Sequence (comments are attached)
|
||||
// to the key
|
||||
if fm.IsEmpty() {
|
||||
if err = fm.Read(node.Key); err == nil {
|
||||
s = &openapi.ResourceSchema{Schema: &fm.Schema}
|
||||
}
|
||||
if fm.Schema.Ref.String() != "" {
|
||||
r, err := openapi.Resolve(&fm.Schema.Ref, openapi.Schema())
|
||||
if err == nil && r != nil {
|
||||
s.Schema = r
|
||||
}
|
||||
}
|
||||
}
|
||||
return node.Value, s
|
||||
}
|
||||
|
||||
// fieldNames returns a sorted slice containing the names of all fields that appear in any of
|
||||
// the sources
|
||||
func (l Walker) fieldNames() []string {
|
||||
fields := sets.String{}
|
||||
for _, s := range l.Sources {
|
||||
if s == nil {
|
||||
continue
|
||||
}
|
||||
// don't check error, we know this is a mapping node
|
||||
sFields, _ := s.Fields()
|
||||
fields.Insert(sFields...)
|
||||
}
|
||||
result := fields.List()
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
// fieldValue returns a slice containing each source's value for fieldName
|
||||
func (l Walker) fieldValue(fieldName string) ([]*yaml.RNode, *openapi.ResourceSchema) {
|
||||
var fields []*yaml.RNode
|
||||
var sch *openapi.ResourceSchema
|
||||
for i := range l.Sources {
|
||||
if l.Sources[i] == nil {
|
||||
fields = append(fields, nil)
|
||||
continue
|
||||
}
|
||||
field := l.Sources[i].Field(fieldName)
|
||||
f, s := l.valueIfPresent(field)
|
||||
fields = append(fields, f)
|
||||
if sch == nil && !s.IsMissingOrNull() {
|
||||
sch = s
|
||||
}
|
||||
}
|
||||
return fields, sch
|
||||
}
|
||||
13
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/nonassociative_sequence.go
generated
vendored
Normal file
13
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/nonassociative_sequence.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package walk
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// walkNonAssociativeSequence returns the value of VisitList
|
||||
func (l Walker) walkNonAssociativeSequence() (*yaml.RNode, error) {
|
||||
return l.VisitList(l.Sources, l.Schema, NonAssociateList)
|
||||
}
|
||||
11
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/scalar.go
generated
vendored
Normal file
11
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/scalar.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package walk
|
||||
|
||||
import "sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
|
||||
// walkScalar returns the value of VisitScalar
|
||||
func (l Walker) walkScalar() (*yaml.RNode, error) {
|
||||
return l.VisitScalar(l.Sources, l.Schema)
|
||||
}
|
||||
28
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/visitor.go
generated
vendored
Normal file
28
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/visitor.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package walk
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type ListKind int32
|
||||
|
||||
const (
|
||||
AssociativeList ListKind = 1 + iota
|
||||
NonAssociateList
|
||||
)
|
||||
|
||||
// Visitor is invoked by walk with source and destination node pairs
|
||||
type Visitor interface {
|
||||
VisitMap(Sources, *openapi.ResourceSchema) (*yaml.RNode, error)
|
||||
|
||||
VisitScalar(Sources, *openapi.ResourceSchema) (*yaml.RNode, error)
|
||||
|
||||
VisitList(Sources, *openapi.ResourceSchema, ListKind) (*yaml.RNode, error)
|
||||
}
|
||||
|
||||
// ClearNode is returned if GrepFilter should do nothing after calling Set
|
||||
var ClearNode *yaml.RNode
|
||||
186
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/walk.go
generated
vendored
Normal file
186
vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/walk.go
generated
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package walk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml/schema"
|
||||
)
|
||||
|
||||
// Walker walks the Source RNode and modifies the RNode provided to GrepFilter.
|
||||
type Walker struct {
|
||||
// Visitor is invoked by GrepFilter
|
||||
Visitor
|
||||
|
||||
Schema *openapi.ResourceSchema
|
||||
|
||||
// Source is the RNode to walk. All Source fields and associative list elements
|
||||
// will be visited.
|
||||
Sources Sources
|
||||
|
||||
// Path is the field path to the current Source Node.
|
||||
Path []string
|
||||
|
||||
// InferAssociativeLists if set to true will infer merge strategies for
|
||||
// fields which it doesn't have the schema based on the fields in the
|
||||
// list elements.
|
||||
InferAssociativeLists bool
|
||||
|
||||
// VisitKeysAsScalars if true will call VisitScalar on map entry keys,
|
||||
// providing nil as the OpenAPI schema.
|
||||
VisitKeysAsScalars bool
|
||||
|
||||
// MergeOptions is a struct to store options for merge
|
||||
MergeOptions yaml.MergeOptions
|
||||
}
|
||||
|
||||
// Kind returns the kind of the first non-null node in Sources.
|
||||
func (l Walker) Kind() yaml.Kind {
|
||||
for _, s := range l.Sources {
|
||||
if !yaml.IsMissingOrNull(s) {
|
||||
return s.YNode().Kind
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Walk will recursively traverse every item in the Sources and perform corresponding
|
||||
// actions on them
|
||||
func (l Walker) Walk() (*yaml.RNode, error) {
|
||||
l.Schema = l.GetSchema()
|
||||
|
||||
// invoke the handler for the corresponding node type
|
||||
switch l.Kind() {
|
||||
case yaml.MappingNode:
|
||||
if err := yaml.ErrorIfAnyInvalidAndNonNull(yaml.MappingNode, l.Sources...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l.walkMap()
|
||||
case yaml.SequenceNode:
|
||||
if err := yaml.ErrorIfAnyInvalidAndNonNull(yaml.SequenceNode, l.Sources...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// AssociativeSequence means the items in the sequence are associative. They can be merged
|
||||
// according to merge key.
|
||||
if schema.IsAssociative(l.Schema, l.Sources, l.InferAssociativeLists) {
|
||||
return l.walkAssociativeSequence()
|
||||
}
|
||||
return l.walkNonAssociativeSequence()
|
||||
|
||||
case yaml.ScalarNode:
|
||||
if err := yaml.ErrorIfAnyInvalidAndNonNull(yaml.ScalarNode, l.Sources...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l.walkScalar()
|
||||
case 0:
|
||||
// walk empty nodes as maps
|
||||
return l.walkMap()
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (l Walker) GetSchema() *openapi.ResourceSchema {
|
||||
for i := range l.Sources {
|
||||
r := l.Sources[i]
|
||||
if yaml.IsMissingOrNull(r) {
|
||||
continue
|
||||
}
|
||||
|
||||
fm := fieldmeta.FieldMeta{}
|
||||
if err := fm.Read(r); err == nil && !fm.IsEmpty() {
|
||||
// per-field schema, this is fine
|
||||
if fm.Schema.Ref.String() != "" {
|
||||
// resolve the reference
|
||||
s, err := openapi.Resolve(&fm.Schema.Ref, openapi.Schema())
|
||||
if err == nil && s != nil {
|
||||
fm.Schema = *s
|
||||
}
|
||||
}
|
||||
return &openapi.ResourceSchema{Schema: &fm.Schema}
|
||||
}
|
||||
}
|
||||
|
||||
if l.Schema != nil {
|
||||
return l.Schema
|
||||
}
|
||||
for i := range l.Sources {
|
||||
r := l.Sources[i]
|
||||
if yaml.IsMissingOrNull(r) {
|
||||
continue
|
||||
}
|
||||
|
||||
m, _ := r.GetMeta()
|
||||
if m.Kind == "" || m.APIVersion == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
s := openapi.SchemaForResourceType(yaml.TypeMeta{Kind: m.Kind, APIVersion: m.APIVersion})
|
||||
if s != nil {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
DestIndex = iota
|
||||
OriginIndex
|
||||
UpdatedIndex
|
||||
)
|
||||
|
||||
// Sources are a list of RNodes. First item is the dest node, followed by
|
||||
// multiple source nodes.
|
||||
type Sources []*yaml.RNode
|
||||
|
||||
// Dest returns the destination node
|
||||
func (s Sources) Dest() *yaml.RNode {
|
||||
if len(s) <= DestIndex {
|
||||
return nil
|
||||
}
|
||||
return s[DestIndex]
|
||||
}
|
||||
|
||||
// Origin returns the origin node
|
||||
func (s Sources) Origin() *yaml.RNode {
|
||||
if len(s) <= OriginIndex {
|
||||
return nil
|
||||
}
|
||||
return s[OriginIndex]
|
||||
}
|
||||
|
||||
// Updated returns the updated node
|
||||
func (s Sources) Updated() *yaml.RNode {
|
||||
if len(s) <= UpdatedIndex {
|
||||
return nil
|
||||
}
|
||||
return s[UpdatedIndex]
|
||||
}
|
||||
|
||||
func (s Sources) String() string {
|
||||
var values []string
|
||||
for i := range s {
|
||||
str, err := s[i].String()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
}
|
||||
values = append(values, str)
|
||||
}
|
||||
return strings.Join(values, "\n")
|
||||
}
|
||||
|
||||
// setDestNode sets the destination source node
|
||||
func (s Sources) setDestNode(node *yaml.RNode, err error) (*yaml.RNode, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s[0] = node
|
||||
return node, nil
|
||||
}
|
||||
Reference in New Issue
Block a user