mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-12 01:14:22 +00:00
Improve error handling during var resolution.
This commit is contained in:
@@ -22,6 +22,12 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/pkg/constants"
|
"sigs.k8s.io/kustomize/pkg/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestNewOptionsToSilenceCodeInspectionError(t *testing.T) {
|
||||||
|
if NewOptions("foo", "bar") == nil {
|
||||||
|
t.Fatal("could not make new options")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuildValidate(t *testing.T) {
|
func TestBuildValidate(t *testing.T) {
|
||||||
var cases = []struct {
|
var cases = []struct {
|
||||||
name string
|
name string
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ func (a *flagsAndArgs) ExpandFileSource(fSys fs.FileSystem) error {
|
|||||||
if key != "" {
|
if key != "" {
|
||||||
if len(result) != 1 {
|
if len(result) != 1 {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"'pattern '%s' catches files %v, should catch only one.", pattern, result)
|
"'pattern '%s' catches files %v, should catch only one", pattern, result)
|
||||||
}
|
}
|
||||||
fileSource := fmt.Sprintf("%s=%s", key, result[0])
|
fileSource := fmt.Sprintf("%s=%s", key, result[0])
|
||||||
results = append(results, fileSource)
|
results = append(results, fileSource)
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ func MappingFuncFor(context ...map[string]string) func(string) string {
|
|||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return syntaxWrap(input)
|
return syntaxWrap(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,9 +46,7 @@ func TestMapReference(t *testing.T) {
|
|||||||
"BLU": "$(ZOO)-2",
|
"BLU": "$(ZOO)-2",
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceEnv := map[string]string{}
|
mapping := MappingFuncFor(declaredEnv)
|
||||||
|
|
||||||
mapping := MappingFuncFor(declaredEnv, serviceEnv)
|
|
||||||
|
|
||||||
for _, env := range envs {
|
for _, env := range envs {
|
||||||
declaredEnv[env.Name] = Expand(env.Value, mapping)
|
declaredEnv[env.Name] = Expand(env.Value, mapping)
|
||||||
|
|||||||
@@ -46,15 +46,6 @@ func (m ResMap) GetMatchingIds(matches IdMatcher) []resid.ResId {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// DemandOneGvknMatchForId find the matched resource by Group/Version/Kind and Name
|
|
||||||
func (m ResMap) DemandOneGvknMatchForId(inputId resid.ResId) (*resource.Resource, bool) {
|
|
||||||
result := m.GetMatchingIds(inputId.GvknEquals)
|
|
||||||
if len(result) == 1 {
|
|
||||||
return m[result[0]], true
|
|
||||||
}
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeAsYaml encodes a ResMap to YAML; encoded objects separated by `---`.
|
// EncodeAsYaml encodes a ResMap to YAML; encoded objects separated by `---`.
|
||||||
func (m ResMap) EncodeAsYaml() ([]byte, error) {
|
func (m ResMap) EncodeAsYaml() ([]byte, error) {
|
||||||
var ids []resid.ResId
|
var ids []resid.ResId
|
||||||
|
|||||||
@@ -92,35 +92,34 @@ func TestDemandOneGvknMatchForId(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ok := rm1.DemandOneGvknMatchForId(
|
result := rm1.GetMatchingIds(
|
||||||
resid.NewResIdWithPrefixNamespace(cmap, "cm2", "prefix1", "ns1"))
|
resid.NewResIdWithPrefixNamespace(cmap, "cm2", "prefix1", "ns1").GvknEquals)
|
||||||
if !ok {
|
if len(result) != 1 {
|
||||||
t.Fatal("Expected single map entry but got none")
|
t.Fatalf("Expected single map entry but got %v", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// confirm that ns and prefix are not included in match
|
// confirm that ns and prefix are not included in match
|
||||||
_, ok = rm1.DemandOneGvknMatchForId(
|
result = rm1.GetMatchingIds(
|
||||||
resid.NewResIdWithPrefixNamespace(cmap, "cm2", "prefix", "ns"))
|
resid.NewResIdWithPrefixNamespace(cmap, "cm2", "prefix", "ns").GvknEquals)
|
||||||
if !ok {
|
if len(result) != 1 {
|
||||||
t.Fatal("Expected single map entry but got none")
|
t.Fatalf("Expected single map entry but got %v", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// confirm that name is matched correctly
|
// confirm that name is matched correctly
|
||||||
result, ok := rm1.DemandOneGvknMatchForId(
|
result = rm1.GetMatchingIds(
|
||||||
resid.NewResIdWithPrefixNamespace(cmap, "cm3", "prefix1", "ns1"))
|
resid.NewResIdWithPrefixNamespace(cmap, "cm3", "prefix1", "ns1").GvknEquals)
|
||||||
if ok {
|
if len(result) > 0 {
|
||||||
t.Fatalf("Expected no map entries but got %v", result)
|
t.Fatalf("Expected no map entries but got %v", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmap2 := gvk.Gvk{Version: "v2", Kind: "ConfigMap"}
|
cmap2 := gvk.Gvk{Version: "v2", Kind: "ConfigMap"}
|
||||||
|
|
||||||
// confirm that gvk is matched correctly
|
// confirm that gvk is matched correctly
|
||||||
result, ok = rm1.DemandOneGvknMatchForId(
|
result = rm1.GetMatchingIds(
|
||||||
resid.NewResIdWithPrefixNamespace(cmap2, "cm2", "prefix1", "ns1"))
|
resid.NewResIdWithPrefixNamespace(cmap2, "cm2", "prefix1", "ns1").GvknEquals)
|
||||||
if ok {
|
if len(result) > 0 {
|
||||||
t.Fatalf("Expected no map entries but got %v", result)
|
t.Fatalf("Expected no map entries but got %v", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFilterBy(t *testing.T) {
|
func TestFilterBy(t *testing.T) {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package target
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"sigs.k8s.io/kustomize/pkg/resid"
|
"sigs.k8s.io/kustomize/pkg/resid"
|
||||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||||
"sigs.k8s.io/kustomize/pkg/transformers"
|
"sigs.k8s.io/kustomize/pkg/transformers"
|
||||||
@@ -101,17 +100,27 @@ func (ra *ResAccumulator) MergeAccumulator(other *ResAccumulator) (err error) {
|
|||||||
// for substitution wherever the $(var.Name) occurs.
|
// for substitution wherever the $(var.Name) occurs.
|
||||||
func (ra *ResAccumulator) makeVarReplacementMap() (map[string]string, error) {
|
func (ra *ResAccumulator) makeVarReplacementMap() (map[string]string, error) {
|
||||||
result := map[string]string{}
|
result := map[string]string{}
|
||||||
for _, v := range ra.varSet.Set() {
|
for _, v := range ra.Vars() {
|
||||||
id := resid.NewResId(v.ObjRef.GVK(), v.ObjRef.Name)
|
matched := ra.resMap.GetMatchingIds(
|
||||||
if r, found := ra.resMap.DemandOneGvknMatchForId(id); found {
|
resid.NewResId(v.ObjRef.GVK(), v.ObjRef.Name).GvknEquals)
|
||||||
s, err := r.GetFieldValue(v.FieldRef.FieldPath)
|
if len(matched) > 1 {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"found %d resId matches for var %s "+
|
||||||
|
"(unable to disambiguate)",
|
||||||
|
len(matched), v)
|
||||||
|
}
|
||||||
|
if len(matched) == 1 {
|
||||||
|
s, err := ra.resMap[matched[0]].GetFieldValue(v.FieldRef.FieldPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("field path err for var: %+v", v)
|
return nil, fmt.Errorf(
|
||||||
|
"field specified in var '%v' "+
|
||||||
|
"not found in corresponding resource", v)
|
||||||
}
|
}
|
||||||
result[v.Name] = s
|
result[v.Name] = s
|
||||||
} else {
|
} else {
|
||||||
// Should this be an error?
|
return nil, fmt.Errorf(
|
||||||
log.Printf("var %v defined but not used", v)
|
"var '%v' cannot be mapped to a field "+
|
||||||
|
"in the set of known resources", v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/pkg/types"
|
"sigs.k8s.io/kustomize/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestResolveVars(t *testing.T) {
|
func makeResAccumulator() (*ResAccumulator, *resource.Factory, error) {
|
||||||
ra := MakeEmptyAccumulator()
|
ra := MakeEmptyAccumulator()
|
||||||
err := ra.MergeConfig(config.MakeDefaultConfig())
|
err := ra.MergeConfig(config.MakeDefaultConfig())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err: %v", err)
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
rf := resource.NewFactory(
|
rf := resource.NewFactory(
|
||||||
kunstruct.NewKunstructuredFactoryImpl())
|
kunstruct.NewKunstructuredFactoryImpl())
|
||||||
@@ -55,8 +55,8 @@ func TestResolveVars(t *testing.T) {
|
|||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"command": []interface{}{
|
"command": []interface{}{
|
||||||
"myserver",
|
"myserver",
|
||||||
"--somebackendService $(FOO)",
|
"--somebackendService $(SERVICE_ONE)",
|
||||||
"--yetAnother $(BAR)",
|
"--yetAnother $(SERVICE_TWO)",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -85,14 +85,23 @@ func TestResolveVars(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
return ra, rf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveVarsHappy(t *testing.T) {
|
||||||
|
ra, _, err := makeResAccumulator()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
err = ra.MergeVars([]types.Var{
|
err = ra.MergeVars([]types.Var{
|
||||||
{
|
{
|
||||||
Name: "FOO",
|
Name: "SERVICE_ONE",
|
||||||
ObjRef: types.Target{
|
ObjRef: types.Target{
|
||||||
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
|
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
|
||||||
Name: "backendOne"},
|
Name: "backendOne"},
|
||||||
}, {
|
},
|
||||||
Name: "BAR",
|
{
|
||||||
|
Name: "SERVICE_TWO",
|
||||||
ObjRef: types.Target{
|
ObjRef: types.Target{
|
||||||
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
|
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
|
||||||
Name: "backendTwo"},
|
Name: "backendTwo"},
|
||||||
@@ -111,6 +120,101 @@ func TestResolveVars(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResolveVarsVarNeedsDisambiguation(t *testing.T) {
|
||||||
|
ra, rf, err := makeResAccumulator()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
ra.MergeResourcesWithErrorOnIdCollision(resmap.ResMap{
|
||||||
|
resid.NewResIdWithPrefixNamespace(
|
||||||
|
gvk.Gvk{Version: "v1", Kind: "Service"},
|
||||||
|
"backendOne", "", "fooNamespace"): rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Service",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "backendOne",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
err = ra.MergeVars([]types.Var{
|
||||||
|
{
|
||||||
|
Name: "SERVICE_ONE",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
|
||||||
|
Name: "backendOne",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
err = ra.ResolveVars()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(
|
||||||
|
err.Error(), "unable to disambiguate") {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveVarsGoodResIdBadField(t *testing.T) {
|
||||||
|
ra, _, err := makeResAccumulator()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
err = ra.MergeVars([]types.Var{
|
||||||
|
{
|
||||||
|
Name: "SERVICE_ONE",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
|
||||||
|
Name: "backendOne"},
|
||||||
|
FieldRef: types.FieldSelector{FieldPath: "nope_nope_nope"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
err = ra.ResolveVars()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(
|
||||||
|
err.Error(),
|
||||||
|
"not found in corresponding resource") {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveVarsUnmappableVar(t *testing.T) {
|
||||||
|
ra, _, err := makeResAccumulator()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
err = ra.MergeVars([]types.Var{
|
||||||
|
{
|
||||||
|
Name: "SERVICE_THREE",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
|
||||||
|
Name: "doesNotExist"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
err = ra.ResolveVars()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(
|
||||||
|
err.Error(),
|
||||||
|
"cannot be mapped to a field in the set of known resources") {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func find(name string, resMap resmap.ResMap) *resource.Resource {
|
func find(name string, resMap resmap.ResMap) *resource.Resource {
|
||||||
for k, v := range resMap {
|
for k, v := range resMap {
|
||||||
if k.Name() == name {
|
if k.Name() == name {
|
||||||
|
|||||||
Reference in New Issue
Block a user