-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #144 from fluxcd/images-in-templates
Ensure that an unchanged image is not in update result
- Loading branch information
Showing
6 changed files
with
259 additions
and
111 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/* | ||
Copyright 2020, 2021 The Flux authors | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package update | ||
|
||
import ( | ||
"github.com/go-openapi/spec" | ||
"sigs.k8s.io/kustomize/kyaml/fieldmeta" | ||
"sigs.k8s.io/kustomize/kyaml/openapi" | ||
"sigs.k8s.io/kustomize/kyaml/setters2" | ||
"sigs.k8s.io/kustomize/kyaml/yaml" | ||
) | ||
|
||
// The implementation of this filter is adapted from | ||
// [kyaml](https://github.com/kubernetes-sigs/kustomize/blob/kyaml/v0.10.16/kyaml/setters2/set.go), | ||
// with the following changes: | ||
// | ||
// - it calls its callback for each field it sets | ||
// | ||
// - it will set all fields referring to a setter present in the | ||
// schema -- this is behind a flag in the kyaml implementation, but | ||
// the only desired mode of operation here | ||
// | ||
// - substitutions are not supported -- they are not used for image | ||
// updates | ||
// | ||
// - no validation is done on the value being set -- since the schema | ||
// is constructed here, it's assumed the values will be appropriate | ||
// | ||
// - only scalar nodes are considered (i.e., no sequence replacements) | ||
// | ||
// - only per-field schema references (those in a comment in the YAML) | ||
// are considered -- these are the only ones relevant to image updates | ||
|
||
type SetAllCallback struct { | ||
SettersSchema *spec.Schema | ||
Callback func(setter, oldValue, newValue string) | ||
} | ||
|
||
func (s *SetAllCallback) Filter(object *yaml.RNode) (*yaml.RNode, error) { | ||
return object, accept(s, object, "", s.SettersSchema) | ||
} | ||
|
||
// visitor is provided to accept to walk the AST. | ||
type visitor interface { | ||
// visitScalar is called for each scalar field value on a resource | ||
// node is the scalar field value | ||
// path is the path to the field; path elements are separated by '.' | ||
visitScalar(node *yaml.RNode, path string, schema *openapi.ResourceSchema) error | ||
} | ||
|
||
// getSchema returns per-field OpenAPI schema for a particular node. | ||
func getSchema(r *yaml.RNode, settersSchema *spec.Schema) *openapi.ResourceSchema { | ||
// get the override schema if it exists on the field | ||
fm := fieldmeta.FieldMeta{SettersSchema: settersSchema} | ||
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, settersSchema) | ||
if err == nil && s != nil { | ||
fm.Schema = *s | ||
} | ||
} | ||
return &openapi.ResourceSchema{Schema: &fm.Schema} | ||
} | ||
return nil | ||
} | ||
|
||
// accept walks the AST and calls the visitor at each scalar node. | ||
func accept(v visitor, object *yaml.RNode, p string, settersSchema *spec.Schema) error { | ||
switch object.YNode().Kind { | ||
case yaml.DocumentNode: | ||
// Traverse the child of the document | ||
return accept(v, yaml.NewRNode(object.YNode()), p, settersSchema) | ||
case yaml.MappingNode: | ||
return object.VisitFields(func(node *yaml.MapNode) error { | ||
// Traverse each field value | ||
return accept(v, node.Value, p+"."+node.Key.YNode().Value, settersSchema) | ||
}) | ||
case yaml.SequenceNode: | ||
return object.VisitElements(func(node *yaml.RNode) error { | ||
// Traverse each list element | ||
return accept(v, node, p, settersSchema) | ||
}) | ||
case yaml.ScalarNode: | ||
fieldSchema := getSchema(object, settersSchema) | ||
return v.visitScalar(object, p, fieldSchema) | ||
} | ||
return nil | ||
} | ||
|
||
// set applies the value from ext to field | ||
func (s *SetAllCallback) set(field *yaml.RNode, ext *setters2.CliExtension, sch *spec.Schema) (bool, error) { | ||
// check full setter | ||
if ext.Setter == nil { | ||
return false, nil | ||
} | ||
|
||
// this has a full setter, set its value | ||
old := field.YNode().Value | ||
field.YNode().Value = ext.Setter.Value | ||
s.Callback(ext.Setter.Name, old, ext.Setter.Value) | ||
|
||
// format the node so it is quoted if it is a string. If there is | ||
// type information on the setter schema, we use it. | ||
if len(sch.Type) > 0 { | ||
yaml.FormatNonStringStyle(field.YNode(), *sch) | ||
} | ||
return true, nil | ||
} | ||
|
||
// visitScalar | ||
func (s *SetAllCallback) visitScalar(object *yaml.RNode, p string, fieldSchema *openapi.ResourceSchema) error { | ||
if fieldSchema == nil { | ||
return nil | ||
} | ||
// get the openAPI for this field describing how to apply the setter | ||
ext, err := setters2.GetExtFromSchema(fieldSchema.Schema) | ||
if err != nil { | ||
return err | ||
} | ||
if ext == nil { | ||
return nil | ||
} | ||
|
||
// perform a direct set of the field if it matches | ||
_, err = s.set(object, ext, fieldSchema.Schema) | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.