Merge pull request #2203 from pwittrock/setter-wiring

Manually merging since the prow automation does not appear to be configured correctly
This commit is contained in:
Phillip Wittrock
2020-02-19 14:55:09 -08:00
committed by Phani Teja Marupaka
27 changed files with 2620 additions and 361 deletions

View File

@@ -0,0 +1,48 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package settersutil
import (
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/setters2"
)
// FieldSetter sets the value for a field setter.
type FieldSetter struct {
// Name is the name of the setter to set
Name string
// Value is the value to set
Value string
Description string
SetBy string
}
// Set updates the OpenAPI definitions and resources with the new setter value
func (fs FieldSetter) Set(openAPIPath, resourcesPath string) (int, error) {
// Update the OpenAPI definitions
soa := setters2.SetOpenAPI{
Name: fs.Name, Value: fs.Value, Description: fs.Description, SetBy: fs.SetBy}
if err := soa.UpdateFile(openAPIPath); err != nil {
return 0, err
}
// Load the updated definitions
if err := openapi.AddSchemaFromFile(openAPIPath); err != nil {
return 0, err
}
// Update the resources with the new value
inout := &kio.LocalPackageReadWriter{PackagePath: resourcesPath}
s := &setters2.Set{Name: fs.Name}
err := kio.Pipeline{
Inputs: []kio.Reader{inout},
Filters: []kio.Filter{kio.FilterAll(s)},
Outputs: []kio.Writer{inout},
}.Execute()
return s.Count, err
}

View File

@@ -0,0 +1,60 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package settersutil
import (
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/setters2"
)
// SetterCreator creates or updates a setter in the OpenAPI definitions, and inserts references
// to the setter from matching resource fields.
type SetterCreator struct {
// Name is the name of the setter to create or update.
Name string
Description string
SetBy string
// FieldName if set will add the OpenAPI reference to fields with this name or path
// FieldName may be the full name of the field, full path to the field, or the path suffix.
// e.g. all of the following would match spec.template.spec.containers.image --
// [image, containers.image, spec.containers.image, template.spec.containers.image,
// spec.template.spec.containers.image]
// Optional. If unspecified match all field names.
FieldName string
// FieldValue if set will add the OpenAPI reference to fields if they have this value.
// Optional. If unspecified match all field values.
FieldValue string
}
func (c SetterCreator) Create(openAPIPath, resourcesPath string) error {
// Update the OpenAPI definitions to hace the setter
sd := setters2.SetterDefinition{
Name: c.Name, Value: c.FieldValue, Description: c.Description, SetBy: c.SetBy}
if err := sd.AddToFile(openAPIPath); err != nil {
return err
}
// Load the updated definitions
if err := openapi.AddSchemaFromFile(openAPIPath); err != nil {
return err
}
// Update the resources with the setter reference
inout := &kio.LocalPackageReadWriter{PackagePath: resourcesPath}
return kio.Pipeline{
Inputs: []kio.Reader{inout},
Filters: []kio.Filter{kio.FilterAll(
&setters2.Add{
FieldName: c.FieldName,
FieldValue: c.FieldValue,
Ref: setters2.DefinitionsPrefix + setters2.SetterDefinitionPrefix + c.Name,
})},
Outputs: []kio.Writer{inout},
}.Execute()
}

View File

@@ -0,0 +1,208 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package settersutil
import (
"io/ioutil"
"math"
"strings"
"sigs.k8s.io/kind/pkg/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/setters2"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// SubstitutionCreator creates or updates a substitution in the OpenAPI definitions, and
// inserts references to the substitution from matching resource fields.
type SubstitutionCreator struct {
// Name is the name of the substitution to create
Name string
// Pattern is the substitution pattern
Pattern string
// Values are the substitution values for the pattern
Values []setters2.Value
// FieldName if set will add the OpenAPI reference to fields with this name or path
// FieldName may be the full name of the field, full path to the field, or the path suffix.
// e.g. all of the following would match spec.template.spec.containers.image --
// [image, containers.image, spec.containers.image, template.spec.containers.image,
// spec.template.spec.containers.image]
// Optional. If unspecified match all field names.
FieldName string
// FieldValue if set will add the OpenAPI reference to fields if they have this value.
// Optional. If unspecified match all field values.
FieldValue string
}
func (c SubstitutionCreator) Create(openAPIPath, resourcesPath string) error {
d := setters2.SubstitutionDefinition{
Name: c.Name,
Values: c.Values,
Pattern: c.Pattern,
}
err := c.CreateSettersForSubstitution(openAPIPath)
if err != nil {
return err
}
if err := d.AddToFile(openAPIPath); err != nil {
return err
}
// Load the updated definitions
if err := openapi.AddSchemaFromFile(openAPIPath); err != nil {
return err
}
// Update the resources with the setter reference
inout := &kio.LocalPackageReadWriter{PackagePath: resourcesPath}
return kio.Pipeline{
Inputs: []kio.Reader{inout},
Filters: []kio.Filter{kio.FilterAll(
&setters2.Add{
FieldName: c.FieldName,
FieldValue: c.FieldValue,
Ref: setters2.DefinitionsPrefix + setters2.SubstitutionDefinitionPrefix + c.Name,
})},
Outputs: []kio.Writer{inout},
}.Execute()
}
// CreateSettersForSubstitution creates the setters for all the references in the substitution
// values if they don't already exist in openAPIPath file.
func (c SubstitutionCreator) CreateSettersForSubstitution(openAPIPath string) error {
b, err := ioutil.ReadFile(openAPIPath)
if err != nil {
return err
}
// parse the yaml file
y, err := yaml.Parse(string(b))
if err != nil {
return err
}
m, err := c.GetValuesForMarkers()
if err != nil {
return err
}
// for each ref in values, check if the setter already exists, if not create them
for _, value := range c.Values {
obj, err := y.Pipe(yaml.Lookup(
// get the setter key from ref. Ex: from #/definitions/io.k8s.cli.setters.image_setter
// extract io.k8s.cli.setters.image_setter
"openAPI", "definitions", strings.TrimPrefix(value.Ref, "#/definitions/")))
if err != nil {
return err
}
if obj == nil {
sd := setters2.SetterDefinition{
// get the setter name from ref. Ex: from #/definitions/io.k8s.cli.setters.image_setter
// extract image_setter
Name: strings.Split(value.Ref, ".")[4],
Value: m[value.Marker],
}
sd.AddToFile(openAPIPath)
}
}
return nil
}
// GetValuesForMarkers parses the pattern and field value to derive values for the
// markers in the pattern string. Returns error if the marker values can't be derived
func (c SubstitutionCreator) GetValuesForMarkers() (map[string]string, error) {
m := make(map[string]string)
indices, err := c.GetStartIndices()
if err != nil {
return nil, err
}
s:=c.FieldValue
p:=c.Pattern
i:=0
j:=0
// iterate s, p with indices i, j respectively and when j hits the index of a marker, freeze j and iterate
// i and capture string till we find the substring just after current marker and before next marker
// Ex: s = "something/ubuntu:0.1.0", p = "something/IMAGE::VERSION", till j reaches 10
// just proceed i and j and check if s[i]==p[j]
// when j is 10, freeze j and move i till it sees substring '::' which derives IMAGE = ubuntu and so on.
for i< len(s) && j< len(p) {
if marker, ok := indices[j]; ok {
value := ""
e := j+len(marker)
for i<len(s) && (e == len(p) ||
s[i:min(len(s), i+lenToNextMarker(indices, e))] != p[e:min(e+lenToNextMarker(indices, e), len(p))]) {
value += string(s[i])
i++
}
// if marker is repeated in the pattern, make sure that the corresponding values
// are same and throw error if not.
if prevValue, ok := m[marker]; ok && prevValue != value {
return nil, errors.Errorf("Same marker is found to have different values in field value.")
}
m[marker] = value
j+=len(marker)
} else {
if s[i] != p[j] {
return nil, errors.Errorf("Unable to derive values for markers. Create setters for all markers.")
}
i++
j++
}
}
// check if both strings are completely visited or throw error
if i<len(s) || j<len(p) {
return nil, errors.Errorf("Unable to derive values for markers. Create setters for all markers and then try again.")
}
return m, nil
}
// GetStartIndices returns the start indices of all the markers in the pattern
func (c SubstitutionCreator) GetStartIndices() (map[int]string, error) {
inds := make(map[int]string)
p := c.Pattern
for _, value := range c.Values {
m := value.Marker
found := false
for i, _ := range p {
if strings.HasPrefix(p[i:], m) {
inds[i] = m
found = true
}
}
if !found {
return nil, errors.Errorf("Unable to find marker " + m + " in the pattern")
}
}
return inds, nil
}
// lenToNextMarker takes in the indices map, an index and returns the distance to
// next greater index
func lenToNextMarker(m map[int]string, j int) int {
res := math.MaxInt32
for k, _ := range m {
if k > j {
res = min(k-j, res)
}
}
return res
}
func min(a int, b int) int {
if a<b {
return a
}
return b
}