Upgrade kustomize-in-kubectl to v4.1.2

This commit is contained in:
monopole 2021-04-15 14:19:03 -07:00
parent 0d0d1889ed
commit 6ab3cced0c
63 changed files with 1443 additions and 1777 deletions

8
go.mod
View File

@ -532,10 +532,10 @@ replace (
modernc.org/xc => modernc.org/xc v1.0.0
rsc.io/pdf => rsc.io/pdf v0.1.1
sigs.k8s.io/apiserver-network-proxy/konnectivity-client => sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15
sigs.k8s.io/kustomize/api => sigs.k8s.io/kustomize/api v0.8.5
sigs.k8s.io/kustomize/cmd/config => sigs.k8s.io/kustomize/cmd/config v0.9.7
sigs.k8s.io/kustomize/kustomize/v4 => sigs.k8s.io/kustomize/kustomize/v4 v4.0.5
sigs.k8s.io/kustomize/kyaml => sigs.k8s.io/kustomize/kyaml v0.10.15
sigs.k8s.io/kustomize/api => sigs.k8s.io/kustomize/api v0.8.8
sigs.k8s.io/kustomize/cmd/config => sigs.k8s.io/kustomize/cmd/config v0.9.10
sigs.k8s.io/kustomize/kustomize/v4 => sigs.k8s.io/kustomize/kustomize/v4 v4.1.2
sigs.k8s.io/kustomize/kyaml => sigs.k8s.io/kustomize/kyaml v0.10.17
sigs.k8s.io/structured-merge-diff/v4 => sigs.k8s.io/structured-merge-diff/v4 v4.1.1
sigs.k8s.io/yaml => sigs.k8s.io/yaml v1.2.0
)

14
go.sum
View File

@ -602,13 +602,13 @@ modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15 h1:4uqm9Mv+w2MmBYD+F4qf/v6tDFUdPOk29C095RbU5mY=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
sigs.k8s.io/kustomize/api v0.8.5 h1:bfCXGXDAbFbb/Jv5AhMj2BB8a5VAJuuQ5/KU69WtDjQ=
sigs.k8s.io/kustomize/api v0.8.5/go.mod h1:M377apnKT5ZHJS++6H4rQoCHmWtt6qTpp3mbe7p6OLY=
sigs.k8s.io/kustomize/cmd/config v0.9.7/go.mod h1:MvXCpHs77cfyxRmCNUQjIqCmZyYsbn5PyQpWiq44nW0=
sigs.k8s.io/kustomize/kustomize/v4 v4.0.5 h1:0xQWp03aKWilF6UJrupcA2rCoCn3jejkJ+m/CCI/Fis=
sigs.k8s.io/kustomize/kustomize/v4 v4.0.5/go.mod h1:C7rYla7sI8EnxHE/xEhRBSHMNfcL91fx0uKmUlUhrBk=
sigs.k8s.io/kustomize/kyaml v0.10.15 h1:dSLgG78KyaxN4HylPXdK+7zB3k7sW6q3IcCmcfKA+aI=
sigs.k8s.io/kustomize/kyaml v0.10.15/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg=
sigs.k8s.io/kustomize/api v0.8.8 h1:G2z6JPSSjtWWgMeWSoHdXqyftJNmMmyxXpwENGoOtGE=
sigs.k8s.io/kustomize/api v0.8.8/go.mod h1:He1zoK0nk43Pc6NlV085xDXDXTNprtcyKZVm3swsdNY=
sigs.k8s.io/kustomize/cmd/config v0.9.10/go.mod h1:Mrby0WnRH7hA6OwOYnYpfpiY0WJIMgYrEDfwOeFdMK0=
sigs.k8s.io/kustomize/kustomize/v4 v4.1.2 h1:iP3ckqMIftwsIKnMqtztReSkkPJvhqNc5QiOpMoFpbY=
sigs.k8s.io/kustomize/kustomize/v4 v4.1.2/go.mod h1:PxBvo4WGYlCLeRPL+ziT64wBXqbgfcalOS/SXa/tcyo=
sigs.k8s.io/kustomize/kyaml v0.10.17 h1:4zrV0ym5AYa0e512q7K3Wp1u7mzoWW0xR3UHJcGWGIg=
sigs.k8s.io/kustomize/kyaml v0.10.17/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg=
sigs.k8s.io/structured-merge-diff/v4 v4.1.1 h1:nYqY2A6oy37sKLYuSBXuQhbj4JVclzJK13BOIvJG5XU=
sigs.k8s.io/structured-merge-diff/v4 v4.1.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=

View File

@ -19,7 +19,7 @@ require (
k8s.io/apimachinery v0.0.0
k8s.io/client-go v0.0.0
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7
sigs.k8s.io/kustomize/api v0.8.5
sigs.k8s.io/kustomize/api v0.8.8
sigs.k8s.io/yaml v1.2.0
)

View File

@ -653,10 +653,10 @@ k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/kustomize/api v0.8.5 h1:bfCXGXDAbFbb/Jv5AhMj2BB8a5VAJuuQ5/KU69WtDjQ=
sigs.k8s.io/kustomize/api v0.8.5/go.mod h1:M377apnKT5ZHJS++6H4rQoCHmWtt6qTpp3mbe7p6OLY=
sigs.k8s.io/kustomize/kyaml v0.10.15 h1:dSLgG78KyaxN4HylPXdK+7zB3k7sW6q3IcCmcfKA+aI=
sigs.k8s.io/kustomize/kyaml v0.10.15/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg=
sigs.k8s.io/kustomize/api v0.8.8 h1:G2z6JPSSjtWWgMeWSoHdXqyftJNmMmyxXpwENGoOtGE=
sigs.k8s.io/kustomize/api v0.8.8/go.mod h1:He1zoK0nk43Pc6NlV085xDXDXTNprtcyKZVm3swsdNY=
sigs.k8s.io/kustomize/kyaml v0.10.17 h1:4zrV0ym5AYa0e512q7K3Wp1u7mzoWW0xR3UHJcGWGIg=
sigs.k8s.io/kustomize/kyaml v0.10.17/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.1 h1:nYqY2A6oy37sKLYuSBXuQhbj4JVclzJK13BOIvJG5XU=
sigs.k8s.io/structured-merge-diff/v4 v4.1.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=

View File

@ -43,8 +43,8 @@ require (
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7
k8s.io/metrics v0.0.0
k8s.io/utils v0.0.0-20201110183641-67b214c5f920
sigs.k8s.io/kustomize/api v0.8.5
sigs.k8s.io/kustomize/kustomize/v4 v4.0.5
sigs.k8s.io/kustomize/api v0.8.8
sigs.k8s.io/kustomize/kustomize/v4 v4.1.2
sigs.k8s.io/yaml v1.2.0
)

View File

@ -740,13 +740,13 @@ k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/kustomize/api v0.8.5 h1:bfCXGXDAbFbb/Jv5AhMj2BB8a5VAJuuQ5/KU69WtDjQ=
sigs.k8s.io/kustomize/api v0.8.5/go.mod h1:M377apnKT5ZHJS++6H4rQoCHmWtt6qTpp3mbe7p6OLY=
sigs.k8s.io/kustomize/cmd/config v0.9.7/go.mod h1:MvXCpHs77cfyxRmCNUQjIqCmZyYsbn5PyQpWiq44nW0=
sigs.k8s.io/kustomize/kustomize/v4 v4.0.5 h1:0xQWp03aKWilF6UJrupcA2rCoCn3jejkJ+m/CCI/Fis=
sigs.k8s.io/kustomize/kustomize/v4 v4.0.5/go.mod h1:C7rYla7sI8EnxHE/xEhRBSHMNfcL91fx0uKmUlUhrBk=
sigs.k8s.io/kustomize/kyaml v0.10.15 h1:dSLgG78KyaxN4HylPXdK+7zB3k7sW6q3IcCmcfKA+aI=
sigs.k8s.io/kustomize/kyaml v0.10.15/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg=
sigs.k8s.io/kustomize/api v0.8.8 h1:G2z6JPSSjtWWgMeWSoHdXqyftJNmMmyxXpwENGoOtGE=
sigs.k8s.io/kustomize/api v0.8.8/go.mod h1:He1zoK0nk43Pc6NlV085xDXDXTNprtcyKZVm3swsdNY=
sigs.k8s.io/kustomize/cmd/config v0.9.10/go.mod h1:Mrby0WnRH7hA6OwOYnYpfpiY0WJIMgYrEDfwOeFdMK0=
sigs.k8s.io/kustomize/kustomize/v4 v4.1.2 h1:iP3ckqMIftwsIKnMqtztReSkkPJvhqNc5QiOpMoFpbY=
sigs.k8s.io/kustomize/kustomize/v4 v4.1.2/go.mod h1:PxBvo4WGYlCLeRPL+ziT64wBXqbgfcalOS/SXa/tcyo=
sigs.k8s.io/kustomize/kyaml v0.10.17 h1:4zrV0ym5AYa0e512q7K3Wp1u7mzoWW0xR3UHJcGWGIg=
sigs.k8s.io/kustomize/kyaml v0.10.17/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.1 h1:nYqY2A6oy37sKLYuSBXuQhbj4JVclzJK13BOIvJG5XU=
sigs.k8s.io/structured-merge-diff/v4 v4.1.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=

View File

@ -653,10 +653,10 @@ k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/kustomize/api v0.8.5 h1:bfCXGXDAbFbb/Jv5AhMj2BB8a5VAJuuQ5/KU69WtDjQ=
sigs.k8s.io/kustomize/api v0.8.5/go.mod h1:M377apnKT5ZHJS++6H4rQoCHmWtt6qTpp3mbe7p6OLY=
sigs.k8s.io/kustomize/kyaml v0.10.15 h1:dSLgG78KyaxN4HylPXdK+7zB3k7sW6q3IcCmcfKA+aI=
sigs.k8s.io/kustomize/kyaml v0.10.15/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg=
sigs.k8s.io/kustomize/api v0.8.8 h1:G2z6JPSSjtWWgMeWSoHdXqyftJNmMmyxXpwENGoOtGE=
sigs.k8s.io/kustomize/api v0.8.8/go.mod h1:He1zoK0nk43Pc6NlV085xDXDXTNprtcyKZVm3swsdNY=
sigs.k8s.io/kustomize/kyaml v0.10.17 h1:4zrV0ym5AYa0e512q7K3Wp1u7mzoWW0xR3UHJcGWGIg=
sigs.k8s.io/kustomize/kyaml v0.10.17/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.1 h1:nYqY2A6oy37sKLYuSBXuQhbj4JVclzJK13BOIvJG5XU=
sigs.k8s.io/structured-merge-diff/v4 v4.1.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=

17
vendor/modules.txt vendored
View File

@ -2630,8 +2630,8 @@ k8s.io/utils/trace
# sigs.k8s.io/apiserver-network-proxy/konnectivity-client => sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15
sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client
sigs.k8s.io/apiserver-network-proxy/konnectivity-client/proto/client
# sigs.k8s.io/kustomize/api v0.8.5 => sigs.k8s.io/kustomize/api v0.8.5
# sigs.k8s.io/kustomize/api => sigs.k8s.io/kustomize/api v0.8.5
# sigs.k8s.io/kustomize/api v0.8.8 => sigs.k8s.io/kustomize/api v0.8.8
# sigs.k8s.io/kustomize/api => sigs.k8s.io/kustomize/api v0.8.8
sigs.k8s.io/kustomize/api/builtins
sigs.k8s.io/kustomize/api/filesys
sigs.k8s.io/kustomize/api/filters/annotations
@ -2652,7 +2652,6 @@ sigs.k8s.io/kustomize/api/hasher
sigs.k8s.io/kustomize/api/ifc
sigs.k8s.io/kustomize/api/image
sigs.k8s.io/kustomize/api/internal/accumulator
sigs.k8s.io/kustomize/api/internal/conflict
sigs.k8s.io/kustomize/api/internal/generators
sigs.k8s.io/kustomize/api/internal/git
sigs.k8s.io/kustomize/api/internal/kusterr
@ -2665,7 +2664,6 @@ sigs.k8s.io/kustomize/api/internal/plugins/utils
sigs.k8s.io/kustomize/api/internal/target
sigs.k8s.io/kustomize/api/internal/utils
sigs.k8s.io/kustomize/api/internal/validate
sigs.k8s.io/kustomize/api/internal/wrappy
sigs.k8s.io/kustomize/api/konfig
sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts
sigs.k8s.io/kustomize/api/krusty
@ -2677,17 +2675,16 @@ sigs.k8s.io/kustomize/api/resid
sigs.k8s.io/kustomize/api/resmap
sigs.k8s.io/kustomize/api/resource
sigs.k8s.io/kustomize/api/types
# sigs.k8s.io/kustomize/cmd/config => sigs.k8s.io/kustomize/cmd/config v0.9.7
# sigs.k8s.io/kustomize/kustomize/v4 v4.0.5 => sigs.k8s.io/kustomize/kustomize/v4 v4.0.5
# sigs.k8s.io/kustomize/kustomize/v4 => sigs.k8s.io/kustomize/kustomize/v4 v4.0.5
# sigs.k8s.io/kustomize/cmd/config => sigs.k8s.io/kustomize/cmd/config v0.9.10
# sigs.k8s.io/kustomize/kustomize/v4 v4.1.2 => sigs.k8s.io/kustomize/kustomize/v4 v4.1.2
# sigs.k8s.io/kustomize/kustomize/v4 => sigs.k8s.io/kustomize/kustomize/v4 v4.1.2
sigs.k8s.io/kustomize/kustomize/v4/commands/build
# sigs.k8s.io/kustomize/kyaml v0.10.15 => sigs.k8s.io/kustomize/kyaml v0.10.15
# sigs.k8s.io/kustomize/kyaml => sigs.k8s.io/kustomize/kyaml v0.10.15
# sigs.k8s.io/kustomize/kyaml v0.10.17 => sigs.k8s.io/kustomize/kyaml v0.10.17
# sigs.k8s.io/kustomize/kyaml => sigs.k8s.io/kustomize/kyaml v0.10.17
sigs.k8s.io/kustomize/kyaml/comments
sigs.k8s.io/kustomize/kyaml/errors
sigs.k8s.io/kustomize/kyaml/ext
sigs.k8s.io/kustomize/kyaml/fieldmeta
sigs.k8s.io/kustomize/kyaml/filtersutil
sigs.k8s.io/kustomize/kyaml/fn/runtime/container
sigs.k8s.io/kustomize/kyaml/fn/runtime/exec
sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil

View File

@ -11,7 +11,7 @@ import (
)
type HashTransformerPlugin struct {
hasher ifc.KunstructuredHasher
hasher ifc.KustHasher
}
func (p *HashTransformerPlugin) Config(
@ -24,7 +24,7 @@ func (p *HashTransformerPlugin) Config(
func (p *HashTransformerPlugin) Transform(m resmap.ResMap) error {
for _, res := range m.Resources() {
if res.NeedHashSuffix() {
h, err := p.hasher.Hash(res)
h, err := res.Hash(p.hasher)
if err != nil {
return err
}

View File

@ -6,7 +6,6 @@ package builtins
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
@ -16,7 +15,6 @@ import (
"github.com/imdario/mergo"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
@ -25,243 +23,285 @@ import (
// HelmChartInflationGeneratorPlugin is a plugin to generate resources
// from a remote or local helm chart.
type HelmChartInflationGeneratorPlugin struct {
h *resmap.PluginHelpers
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
runHelmCommand func([]string) ([]byte, error)
types.HelmChartArgs
h *resmap.PluginHelpers
types.HelmGlobals
types.HelmChart
tmpDir string
}
var KustomizePlugin HelmChartInflationGeneratorPlugin
const (
valuesMergeOptionMerge = "merge"
valuesMergeOptionOverride = "override"
valuesMergeOptionReplace = "replace"
)
var legalMergeOptions = []string{
valuesMergeOptionMerge,
valuesMergeOptionOverride,
valuesMergeOptionReplace,
}
// Config uses the input plugin configurations `config` to setup the generator
// options
func (p *HelmChartInflationGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte) error {
func (p *HelmChartInflationGeneratorPlugin) Config(
h *resmap.PluginHelpers, config []byte) (err error) {
if h.GeneralConfig() == nil {
return fmt.Errorf("unable to access general config")
}
if !h.GeneralConfig().HelmConfig.Enabled {
return fmt.Errorf("must specify --enable-helm")
}
if h.GeneralConfig().HelmConfig.Command == "" {
return fmt.Errorf("must specify --helm-command")
}
p.h = h
err := yaml.Unmarshal(config, p)
if err != nil {
return err
if err = yaml.Unmarshal(config, p); err != nil {
return
}
tmpDir, err := filesys.NewTmpConfirmedDir()
if err != nil {
return err
}
p.tmpDir = string(tmpDir)
if p.ChartName == "" {
return fmt.Errorf("chartName cannot be empty")
}
if p.ChartHome == "" {
p.ChartHome = filepath.Join(p.tmpDir, "chart")
}
if p.ChartRepoName == "" {
p.ChartRepoName = "stable"
}
if p.HelmBin == "" {
p.HelmBin = "helm"
}
if p.HelmHome == "" {
p.HelmHome = filepath.Join(p.tmpDir, ".helm")
}
if p.Values == "" {
p.Values = filepath.Join(p.ChartHome, p.ChartName, "values.yaml")
}
if p.ValuesMerge == "" {
p.ValuesMerge = "override"
}
// runHelmCommand will run `helm` command with args provided. Return stdout
// and error if there is any.
p.runHelmCommand = func(args []string) ([]byte, error) {
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
cmd := exec.Command(p.HelmBin, args...)
cmd.Stdout = stdout
cmd.Stderr = stderr
cmd.Env = append(cmd.Env,
fmt.Sprintf("HELM_CONFIG_HOME=%s", p.HelmHome),
fmt.Sprintf("HELM_CACHE_HOME=%s/.cache", p.HelmHome),
fmt.Sprintf("HELM_DATA_HOME=%s/.data", p.HelmHome),
)
err := cmd.Run()
if err != nil {
return stdout.Bytes(),
errors.Wrap(
fmt.Errorf("failed to run command %s %s", p.HelmBin, strings.Join(args, " ")),
stderr.String(),
)
}
return stdout.Bytes(), nil
}
return nil
return p.validateArgs()
}
// EncodeValues for writing
func (p *HelmChartInflationGeneratorPlugin) EncodeValues(w io.Writer) error {
d, err := yaml.Marshal(p.ValuesLocal)
if err != nil {
return err
}
_, err = w.Write(d)
if err != nil {
return err
}
return nil
}
// useValuesLocal process (merge) inflator config provided values with chart default values.yaml
func (p *HelmChartInflationGeneratorPlugin) useValuesLocal() error {
// not override, merge, none
if !(p.ValuesMerge == "none" || p.ValuesMerge == "no" || p.ValuesMerge == "false") {
var pValues []byte
var err error
if filepath.IsAbs(p.Values) {
pValues, err = ioutil.ReadFile(p.Values)
} else {
pValues, err = p.h.Loader().Load(p.Values)
}
if err != nil {
return err
}
chValues := make(map[string]interface{})
err = yaml.Unmarshal(pValues, &chValues)
if err != nil {
return err
}
if p.ValuesMerge == "override" {
err = mergo.Merge(&chValues, p.ValuesLocal, mergo.WithOverride)
if err != nil {
return err
}
}
if p.ValuesMerge == "merge" {
err = mergo.Merge(&chValues, p.ValuesLocal)
if err != nil {
return err
}
}
p.ValuesLocal = chValues
}
b, err := yaml.Marshal(p.ValuesLocal)
if err != nil {
return err
}
path, err := p.writeValuesBytes(b)
if err != nil {
return err
}
p.Values = path
return nil
}
// copyValues will copy the relative values file into the temp directory
// to avoid messing up with CWD.
func (p *HelmChartInflationGeneratorPlugin) copyValues() error {
// only copy when the values path is not absolute
if filepath.IsAbs(p.Values) {
// This uses the real file system since tmpDir may be used
// by the helm subprocess. Cannot use a chroot jail or fake
// filesystem since we allow the user to use previously
// downloaded charts. This is safe since this plugin is
// owned by kustomize.
func (p *HelmChartInflationGeneratorPlugin) establishTmpDir() (err error) {
if p.tmpDir != "" {
// already done.
return nil
}
// we must use use loader to read values file
b, err := p.h.Loader().Load(p.Values)
if err != nil {
p.tmpDir, err = ioutil.TempDir("", "kustomize-helm-")
return err
}
func (p *HelmChartInflationGeneratorPlugin) validateArgs() (err error) {
if p.Name == "" {
return fmt.Errorf("chart name cannot be empty")
}
// ChartHome might be consulted by the plugin (to read
// values files below it), so it must be located under
// the loader root (unless root restrictions are
// disabled, in which case this can be an absolute path).
if p.ChartHome == "" {
p.ChartHome = "charts"
}
// The ValuesFile may be consulted by the plugin, so it must
// be under the loader root (unless root restrictions are
// disabled).
if p.ValuesFile == "" {
p.ValuesFile = filepath.Join(p.ChartHome, p.Name, "values.yaml")
}
if err = p.errIfIllegalValuesMerge(); err != nil {
return err
}
path, err := p.writeValuesBytes(b)
if err != nil {
return err
// ConfigHome is not loaded by the plugin, and can be located anywhere.
if p.ConfigHome == "" {
if err = p.establishTmpDir(); err != nil {
return errors.Wrap(
err, "unable to create tmp dir for HELM_CONFIG_HOME")
}
p.ConfigHome = filepath.Join(p.tmpDir, "helm")
}
p.Values = path
return nil
}
func (p *HelmChartInflationGeneratorPlugin) writeValuesBytes(b []byte) (string, error) {
path := filepath.Join(p.ChartHome, p.ChartName, "kustomize-values.yaml")
err := ioutil.WriteFile(path, b, 0644)
func (p *HelmChartInflationGeneratorPlugin) errIfIllegalValuesMerge() error {
if p.ValuesMerge == "" {
// Use the default.
p.ValuesMerge = valuesMergeOptionOverride
return nil
}
for _, opt := range legalMergeOptions {
if p.ValuesMerge == opt {
return nil
}
}
return fmt.Errorf("valuesMerge must be one of %v", legalMergeOptions)
}
func (p *HelmChartInflationGeneratorPlugin) absChartHome() string {
if filepath.IsAbs(p.ChartHome) {
return p.ChartHome
}
return filepath.Join(p.h.Loader().Root(), p.ChartHome)
}
func (p *HelmChartInflationGeneratorPlugin) runHelmCommand(
args []string) ([]byte, error) {
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
cmd := exec.Command(p.h.GeneralConfig().HelmConfig.Command, args...)
cmd.Stdout = stdout
cmd.Stderr = stderr
env := []string{
fmt.Sprintf("HELM_CONFIG_HOME=%s", p.ConfigHome),
fmt.Sprintf("HELM_CACHE_HOME=%s/.cache", p.ConfigHome),
fmt.Sprintf("HELM_DATA_HOME=%s/.data", p.ConfigHome)}
cmd.Env = append(os.Environ(), env...)
err := cmd.Run()
if err != nil {
helm := p.h.GeneralConfig().HelmConfig.Command
err = errors.Wrap(
fmt.Errorf(
"unable to run: '%s %s' with env=%s (is '%s' installed?)",
helm, strings.Join(args, " "), env, helm),
stderr.String(),
)
}
return stdout.Bytes(), err
}
// createNewMergedValuesFile replaces/merges original values file with ValuesInline.
func (p *HelmChartInflationGeneratorPlugin) createNewMergedValuesFile() (
path string, err error) {
if p.ValuesMerge == valuesMergeOptionMerge ||
p.ValuesMerge == valuesMergeOptionOverride {
if err = p.replaceValuesInline(); err != nil {
return "", err
}
}
var b []byte
b, err = yaml.Marshal(p.ValuesInline)
if err != nil {
return "", err
}
return path, nil
return p.writeValuesBytes(b)
}
func (p *HelmChartInflationGeneratorPlugin) replaceValuesInline() error {
pValues, err := p.h.Loader().Load(p.ValuesFile)
if err != nil {
return err
}
chValues := make(map[string]interface{})
if err = yaml.Unmarshal(pValues, &chValues); err != nil {
return err
}
switch p.ValuesMerge {
case valuesMergeOptionOverride:
err = mergo.Merge(
&chValues, p.ValuesInline, mergo.WithOverride)
case valuesMergeOptionMerge:
err = mergo.Merge(&chValues, p.ValuesInline)
}
p.ValuesInline = chValues
return err
}
// copyValuesFile to avoid branching. TODO: get rid of this.
func (p *HelmChartInflationGeneratorPlugin) copyValuesFile() (string, error) {
b, err := p.h.Loader().Load(p.ValuesFile)
if err != nil {
return "", err
}
return p.writeValuesBytes(b)
}
// Write a absolute path file in the tmp file system.
func (p *HelmChartInflationGeneratorPlugin) writeValuesBytes(
b []byte) (string, error) {
if err := p.establishTmpDir(); err != nil {
return "", fmt.Errorf("cannot create tmp dir to write helm values")
}
path := filepath.Join(p.tmpDir, p.Name+"-kustomize-values.yaml")
return path, ioutil.WriteFile(path, b, 0644)
}
func (p *HelmChartInflationGeneratorPlugin) cleanup() {
if p.tmpDir != "" {
os.RemoveAll(p.tmpDir)
}
}
// Generate implements generator
func (p *HelmChartInflationGeneratorPlugin) Generate() (resmap.ResMap, error) {
// cleanup
defer os.RemoveAll(p.tmpDir)
// check helm version. we only support V3
err := p.checkHelmVersion()
if err != nil {
func (p *HelmChartInflationGeneratorPlugin) Generate() (rm resmap.ResMap, err error) {
defer p.cleanup()
if err = p.checkHelmVersion(); err != nil {
return nil, err
}
// pull the chart
if !p.checkLocalChart() {
_, err := p.runHelmCommand(p.getPullCommandArgs())
if err != nil {
if path, exists := p.chartExistsLocally(); !exists {
if p.Repo == "" {
return nil, fmt.Errorf(
"no repo specified for pull, no chart found at '%s'", path)
}
if _, err := p.runHelmCommand(p.pullCommand()); err != nil {
return nil, err
}
}
// inflator config valuesLocal
if len(p.ValuesLocal) > 0 {
err := p.useValuesLocal()
if err != nil {
return nil, err
}
if len(p.ValuesInline) > 0 {
p.ValuesFile, err = p.createNewMergedValuesFile()
} else {
err := p.copyValues()
if err != nil {
return nil, err
}
p.ValuesFile, err = p.copyValuesFile()
}
// render the charts
stdout, err := p.runHelmCommand(p.getTemplateCommandArgs())
if err != nil {
return nil, err
}
var stdout []byte
stdout, err = p.runHelmCommand(p.templateCommand())
if err != nil {
return nil, err
}
return p.h.ResmapFactory().NewResMapFromBytes(stdout)
rm, err = p.h.ResmapFactory().NewResMapFromBytes(stdout)
if err == nil {
return rm, nil
}
// try to remove the contents before first "---" because
// helm may produce messages to stdout before it
stdoutStr := string(stdout)
if idx := strings.Index(stdoutStr, "---"); idx != -1 {
return p.h.ResmapFactory().NewResMapFromBytes([]byte(stdoutStr[idx:]))
}
return nil, err
}
func (p *HelmChartInflationGeneratorPlugin) getTemplateCommandArgs() []string {
func (p *HelmChartInflationGeneratorPlugin) templateCommand() []string {
args := []string{"template"}
if p.ReleaseName != "" {
args = append(args, p.ReleaseName)
}
args = append(args, filepath.Join(p.ChartHome, p.ChartName))
if p.ReleaseNamespace != "" {
args = append(args, "--namespace", p.ReleaseNamespace)
args = append(args, filepath.Join(p.absChartHome(), p.Name))
if p.ValuesFile != "" {
args = append(args, "--values", p.ValuesFile)
}
if p.Values != "" {
args = append(args, "--values", p.Values)
if p.ReleaseName == "" {
// AFAICT, this doesn't work as intended due to a bug in helm.
// See https://github.com/helm/helm/issues/6019
// I've tried placing the flag before and after the name argument.
args = append(args, "--generate-name")
}
args = append(args, p.ExtraArgs...)
return args
}
func (p *HelmChartInflationGeneratorPlugin) getPullCommandArgs() []string {
args := []string{"pull", "--untar", "--untardir", p.ChartHome}
chartName := fmt.Sprintf("%s/%s", p.ChartRepoName, p.ChartName)
if p.ChartVersion != "" {
args = append(args, "--version", p.ChartVersion)
func (p *HelmChartInflationGeneratorPlugin) pullCommand() []string {
args := []string{
"pull",
"--untar",
"--untardir", p.absChartHome(),
"--repo", p.Repo,
p.Name}
if p.Version != "" {
args = append(args, "--version", p.Version)
}
if p.ChartRepoURL != "" {
args = append(args, "--repo", p.ChartRepoURL)
chartName = p.ChartName
}
args = append(args, chartName)
return args
}
// checkLocalChart will return true if the chart does exist in
// chartExistsLocally will return true if the chart does exist in
// local chart home.
func (p *HelmChartInflationGeneratorPlugin) checkLocalChart() bool {
path := filepath.Join(p.ChartHome, p.ChartName)
func (p *HelmChartInflationGeneratorPlugin) chartExistsLocally() (string, bool) {
path := filepath.Join(p.absChartHome(), p.Name)
s, err := os.Stat(path)
if err != nil {
return false
return "", false
}
return s.IsDir()
return path, s.IsDir()
}
// checkHelmVersion will return an error if the helm version is not V3
@ -270,11 +310,17 @@ func (p *HelmChartInflationGeneratorPlugin) checkHelmVersion() error {
if err != nil {
return err
}
r, err := regexp.Compile(`v\d+(\.\d+)+`)
r, err := regexp.Compile(`v?\d+(\.\d+)+`)
if err != nil {
return err
}
v := string(r.Find(stdout))[1:]
v := r.FindString(string(stdout))
if v == "" {
return fmt.Errorf("cannot find version string in %s", string(stdout))
}
if v[0] == 'v' {
v = v[1:]
}
majorVersion := strings.Split(v, ".")[0]
if majorVersion != "3" {
return fmt.Errorf("this plugin requires helm V3 but got v%s", v)

View File

@ -30,20 +30,15 @@ func (p *NamespaceTransformerPlugin) Transform(m resmap.ResMap) error {
return nil
}
for _, r := range m.Resources() {
empty, err := r.IsEmpty()
if err != nil {
return err
}
if empty {
if r.IsEmpty() {
// Don't mutate empty objects?
continue
}
r.StorePreviousId()
err = r.ApplyFilter(namespace.Filter{
if err := r.ApplyFilter(namespace.Filter{
Namespace: p.Namespace,
FsSlice: p.FieldSpecs,
})
if err != nil {
}); err != nil {
return err
}
matches := m.GetMatchingResourcesByCurrentId(r.CurId().Equals)

View File

@ -28,45 +28,48 @@ func (p *PatchStrategicMergeTransformerPlugin) Config(
return fmt.Errorf("empty file path and empty patch content")
}
if len(p.Paths) != 0 {
for _, onePath := range p.Paths {
// The following oddly attempts to interpret a path string as an
// actual patch (instead of as a path to a file containing a patch).
// All tests pass if this code is commented out. This code should
// be deleted; the user should use the Patches field which
// exists for this purpose (inline patch declaration).
res, err := h.ResmapFactory().RF().SliceFromBytes([]byte(onePath))
if err == nil {
p.loadedPatches = append(p.loadedPatches, res...)
continue
}
res, err = h.ResmapFactory().RF().SliceFromPatches(
h.Loader(), []types.PatchStrategicMerge{onePath})
if err != nil {
return err
}
p.loadedPatches = append(p.loadedPatches, res...)
}
}
if p.Patches != "" {
res, err := h.ResmapFactory().RF().SliceFromBytes([]byte(p.Patches))
patches, err := loadFromPaths(h, p.Paths)
if err != nil {
return err
}
p.loadedPatches = append(p.loadedPatches, res...)
p.loadedPatches = append(p.loadedPatches, patches...)
}
if p.Patches != "" {
patches, err := h.ResmapFactory().RF().SliceFromBytes([]byte(p.Patches))
if err != nil {
return err
}
p.loadedPatches = append(p.loadedPatches, patches...)
}
if len(p.loadedPatches) == 0 {
return fmt.Errorf(
"patch appears to be empty; files=%v, Patch=%s", p.Paths, p.Patches)
}
// Merge the patches, looking for conflicts.
_, err = h.ResmapFactory().ConflatePatches(p.loadedPatches)
if err != nil {
return err
}
return nil
}
func loadFromPaths(
h *resmap.PluginHelpers,
paths []types.PatchStrategicMerge) (
result []*resource.Resource, err error) {
var patches []*resource.Resource
for _, path := range paths {
// For legacy reasons, attempt to treat the path string as
// actual patch content.
patches, err = h.ResmapFactory().RF().SliceFromBytes([]byte(path))
if err != nil {
// Failing that, treat it as a file path.
patches, err = h.ResmapFactory().RF().SliceFromPatches(
h.Loader(), []types.PatchStrategicMerge{path})
if err != nil {
return
}
}
result = append(result, patches...)
}
return
}
func (p *PatchStrategicMergeTransformerPlugin) Transform(m resmap.ResMap) error {
for _, patch := range p.loadedPatches {
target, err := m.GetById(patch.OrgId())

View File

@ -21,6 +21,7 @@ type PatchTransformerPlugin struct {
Path string `json:"path,omitempty" yaml:"path,omitempty"`
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"`
Options map[string]bool `json:"options,omitempty" yaml:"options,omitempty"`
}
func (p *PatchTransformerPlugin) Config(
@ -60,6 +61,12 @@ func (p *PatchTransformerPlugin) Config(
}
if errSM == nil {
p.loadedPatch = patchSM
if p.Options["allowNameChange"] {
p.loadedPatch.SetAllowNameChange("true")
}
if p.Options["allowKindChange"] {
p.loadedPatch.SetAllowKindChange("true")
}
} else {
p.decodedPatch = patchJson
}

View File

@ -1,7 +1,6 @@
package nameref
import (
"encoding/json"
"fmt"
"strings"
@ -11,7 +10,6 @@ import (
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@ -186,11 +184,7 @@ func (f Filter) recordTheReferral(referral *resource.Resource) {
// getRoleRefGvk returns a Gvk in the roleRef field. Return error
// if the roleRef, roleRef/apiGroup or roleRef/kind is missing.
func getRoleRefGvk(res json.Marshaler) (*resid.Gvk, error) {
n, err := filtersutil.GetRNode(res)
if err != nil {
return nil, err
}
func getRoleRefGvk(n *yaml.RNode) (*resid.Gvk, error) {
roleRef, err := n.Pipe(yaml.Lookup("roleRef"))
if err != nil {
return nil, err
@ -276,7 +270,7 @@ func (f Filter) roleRefFilter() sieveFunc {
if !strings.HasSuffix(f.NameFieldToUpdate.Path, "roleRef/name") {
return acceptAll
}
roleRefGvk, err := getRoleRefGvk(f.Referrer)
roleRefGvk, err := getRoleRefGvk(f.Referrer.AsRNode())
if err != nil {
return acceptAll
}

View File

@ -118,7 +118,6 @@ func (ns Filter) roleBindingHack(obj *yaml.RNode, meta yaml.ResourceMeta) error
// add the namespace to each "subject" with name: default
err = obj.VisitElements(func(o *yaml.RNode) error {
// copied from kunstruct based kustomize NamespaceTransformer plugin
// The only case we need to force the namespace
// if for the "service account". "default" is
// kind of hardcoded here for right now.

View File

@ -4,7 +4,6 @@
package patchstrategicmerge
import (
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/kustomize/kyaml/yaml/merge2"
@ -29,7 +28,7 @@ func (pf Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
if err != nil {
return nil, err
}
if !konfig.FlagEnableKyamlDefaultValue || r != nil {
if r != nil {
result = append(result, r)
}
}

View File

@ -20,12 +20,12 @@ func SortArrayAndComputeHash(s []string) (string, error) {
if err != nil {
return "", err
}
return Encode(Hash(string(data)))
return encode(hex256(string(data)))
}
// Copied from https://github.com/kubernetes/kubernetes
// /blob/master/pkg/kubectl/util/hash/hash.go
func Encode(hex string) (string, error) {
func encode(hex string) (string, error) {
if len(hex) < 10 {
return "", fmt.Errorf(
"input length must be at least 10")
@ -48,23 +48,18 @@ func Encode(hex string) (string, error) {
return string(enc), nil
}
// Hash returns the hex form of the sha256 of the argument.
func Hash(data string) string {
// hex256 returns the hex form of the sha256 of the argument.
func hex256(data string) string {
return fmt.Sprintf("%x", sha256.Sum256([]byte(data)))
}
// HashRNode returns the hash value of input RNode
func HashRNode(node *yaml.RNode) (string, error) {
// get node kind
kindNode, err := node.Pipe(yaml.FieldMatcher{Name: "kind"})
if err != nil {
return "", err
}
kind := kindNode.YNode().Value
// Hasher computes the hash of an RNode.
type Hasher struct{}
// calculate hash for different kinds
encoded := ""
switch kind {
// Hash returns a hash of the argument.
func (h *Hasher) Hash(node *yaml.RNode) (r string, err error) {
var encoded string
switch node.GetKind() {
case "ConfigMap":
encoded, err = encodeConfigMap(node)
case "Secret":
@ -77,10 +72,11 @@ func HashRNode(node *yaml.RNode) (string, error) {
if err != nil {
return "", err
}
return Encode(Hash(encoded))
return encode(hex256(encoded))
}
func getNodeValues(node *yaml.RNode, paths []string) (map[string]interface{}, error) {
func getNodeValues(
node *yaml.RNode, paths []string) (map[string]interface{}, error) {
values := make(map[string]interface{})
for _, p := range paths {
vn, err := node.Pipe(yaml.Lookup(p))
@ -117,8 +113,11 @@ func encodeConfigMap(node *yaml.RNode) (string, error) {
if err != nil {
return "", err
}
m := map[string]interface{}{"kind": "ConfigMap", "name": values["metadata/name"],
"data": values["data"]}
m := map[string]interface{}{
"kind": "ConfigMap",
"name": values["metadata/name"],
"data": values["data"],
}
if _, ok := values["binaryData"].(map[string]interface{}); ok {
m["binaryData"] = values["binaryData"]
}

View File

@ -5,8 +5,8 @@
package ifc
import (
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// Validator provides functions to validate annotations and labels
@ -38,92 +38,10 @@ type Loader interface {
Cleanup() error
}
// Kunstructured represents a Kubernetes Resource Model object.
type Kunstructured interface {
// Several uses.
Copy() Kunstructured
// GetAnnotations returns the k8s annotations.
GetAnnotations() map[string]string
// GetData returns a top-level "data" field, as in a ConfigMap.
GetDataMap() map[string]string
// GetData returns a top-level "binaryData" field, as in a ConfigMap.
GetBinaryDataMap() map[string]string
// Used by ResAccumulator and ReplacementTransformer.
GetFieldValue(string) (interface{}, error)
// Used by Resource.OrgId
GetGvk() resid.Gvk
// Used by resource.Factory.SliceFromBytes
GetKind() string
// GetLabels returns the k8s labels.
GetLabels() map[string]string
// Used by Resource.CurId and resource factory.
GetName() string
// Used by special case code in
// ResMap.SubsetThatCouldBeReferencedByResource
GetSlice(path string) ([]interface{}, error)
// GetString returns the value of a string field.
// Used by Resource.GetNamespace
GetString(string) (string, error)
// Several uses.
Map() (map[string]interface{}, error)
// Used by Resource.AsYAML and Resource.String
MarshalJSON() ([]byte, error)
// Used by resWrangler.Select
MatchesAnnotationSelector(selector string) (bool, error)
// Used by resWrangler.Select
MatchesLabelSelector(selector string) (bool, error)
// SetAnnotations replaces the k8s annotations.
SetAnnotations(map[string]string)
// SetDataMap sets a top-level "data" field, as in a ConfigMap.
SetDataMap(map[string]string)
// SetDataMap sets a top-level "binaryData" field, as in a ConfigMap.
SetBinaryDataMap(map[string]string)
// Used by PatchStrategicMergeTransformer.
SetGvk(resid.Gvk)
// SetLabels replaces the k8s labels.
SetLabels(map[string]string)
// SetName changes the name.
SetName(string)
// SetNamespace changes the namespace.
SetNamespace(string)
// Needed, for now, by kyaml/filtersutil.ApplyToJSON.
UnmarshalJSON([]byte) error
}
// KunstructuredFactory makes instances of Kunstructured.
type KunstructuredFactory interface {
SliceFromBytes([]byte) ([]Kunstructured, error)
FromMap(m map[string]interface{}) Kunstructured
Hasher() KunstructuredHasher
MakeConfigMap(kvLdr KvLoader, args *types.ConfigMapArgs) (Kunstructured, error)
MakeSecret(kvLdr KvLoader, args *types.SecretArgs) (Kunstructured, error)
}
// KunstructuredHasher returns a hash of the argument
// KustHasher returns a hash of the argument
// or an error.
type KunstructuredHasher interface {
Hash(Kunstructured) (string, error)
type KustHasher interface {
Hash(*yaml.RNode) (string, error)
}
// See core.v1.SecretTypeOpaque

View File

@ -25,7 +25,7 @@ type OpenAPIDefinition struct {
Dependencies []string
}
type myProperties map[string]spec.Schema
type myProperties = map[string]spec.Schema
type nameToApiMap map[string]OpenAPIDefinition
// LoadConfigFromCRDs parse CRD schemas from paths into a TransformerConfig

View File

@ -1,23 +0,0 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package conflict
import (
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resource"
)
type cdFactory struct{}
var _ resource.ConflictDetectorFactory = &cdFactory{}
// NewFactory returns a new conflict detector factory.
func NewFactory() resource.ConflictDetectorFactory {
return &cdFactory{}
}
// New returns an instance of smPatchMergeOnlyDetector.
func (c cdFactory) New(_ resid.Gvk) (resource.ConflictDetector, error) {
return &smPatchMergeOnlyDetector{}, nil
}

View File

@ -1,33 +0,0 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package conflict
import (
"sigs.k8s.io/kustomize/api/resource"
)
// smPatchMergeOnlyDetector ignores conflicts,
// but does real strategic merge patching.
// This is part of an effort to eliminate dependence on
// apimachinery package to allow kustomize integration
// into kubectl (#2506 and #1500)
type smPatchMergeOnlyDetector struct{}
var _ resource.ConflictDetector = &smPatchMergeOnlyDetector{}
func (c *smPatchMergeOnlyDetector) HasConflict(
_, _ *resource.Resource) (bool, error) {
return false, nil
}
// There's at least one case that doesn't work. Suppose one has a
// Deployment with a volume with the bizarre "emptyDir: {}" entry.
// If you want to get rid of this entry via a patch containing
// the entry "emptyDir: null", then the following won't work,
// because null entries are eliminated.
func (c *smPatchMergeOnlyDetector) MergePatches(
r, patch *resource.Resource) (*resource.Resource, error) {
err := r.ApplySmPatch(patch)
return r, err
}

View File

@ -13,6 +13,7 @@ import (
"strings"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
"sigs.k8s.io/kustomize/api/internal/plugins/execplugin"
@ -29,11 +30,21 @@ import (
type Loader struct {
pc *types.PluginConfig
rf *resmap.Factory
fs filesys.FileSystem
// absolutePluginHome caches the location of a valid plugin root directory.
// It should only be set once the directory's existence has been confirmed.
absolutePluginHome string
}
func NewLoader(
pc *types.PluginConfig, rf *resmap.Factory) *Loader {
return &Loader{pc: pc, rf: rf}
pc *types.PluginConfig, rf *resmap.Factory, fs filesys.FileSystem) *Loader {
return &Loader{pc: pc, rf: rf, fs: fs}
}
// Config provides the global (not plugin specific) PluginConfig data.
func (l *Loader) Config() *types.PluginConfig {
return l.pc
}
func (l *Loader) LoadGenerators(
@ -95,13 +106,47 @@ func relativePluginPath(id resid.ResId) string {
strings.ToLower(id.Kind))
}
func AbsolutePluginPath(pc *types.PluginConfig, id resid.ResId) string {
return filepath.Join(
pc.AbsPluginHome, relativePluginPath(id), id.Kind)
func (l *Loader) AbsolutePluginPath(id resid.ResId) (string, error) {
pluginHome, err := l.absPluginHome()
if err != nil {
return "", err
}
return filepath.Join(pluginHome, relativePluginPath(id), id.Kind), nil
}
func (l *Loader) absolutePluginPath(id resid.ResId) string {
return AbsolutePluginPath(l.pc, id)
// absPluginHome is the home of kustomize Exec and Go plugins.
// Kustomize plugin configuration files are k8s-style objects
// containing the fields 'apiVersion' and 'kind', e.g.
// apiVersion: apps/v1
// kind: Deployment
// kustomize reads plugin configuration data from a file path
// specified in the 'generators:' or 'transformers:' field of a
// kustomization file. For Exec and Go plugins, kustomize
// uses this data to both locate the plugin and configure it.
// Each Exec or Go plugin (its code, its tests, its supporting data
// files, etc.) must be housed in its own directory at
// ${absPluginHome}/${pluginApiVersion}/LOWERCASE(${pluginKind})
// where
// - ${absPluginHome} is an absolute path, defined below.
// - ${pluginApiVersion} is taken from the plugin config file.
// - ${pluginKind} is taken from the plugin config file.
func (l *Loader) absPluginHome() (string, error) {
// External plugins are disabled--return the dummy plugin root.
if l.pc.PluginRestrictions != types.PluginRestrictionsNone {
return konfig.NoPluginHomeSentinal, nil
}
// We've already determined plugin home--use the cached value.
if l.absolutePluginHome != "" {
return l.absolutePluginHome, nil
}
// Check default locations for a valid plugin root, and cache it if found.
dir, err := konfig.DefaultAbsPluginHome(l.fs)
if err != nil {
return "", err
}
l.absolutePluginHome = dir
return l.absolutePluginHome, nil
}
func isBuiltinPlugin(res *resource.Resource) bool {
@ -148,7 +193,7 @@ func (l *Loader) loadAndConfigurePlugin(
if err != nil {
return nil, errors.Wrapf(err, "marshalling yaml from res %s", res.OrgId())
}
err = c.Config(resmap.NewPluginHelpers(ldr, v, l.rf), yaml)
err = c.Config(resmap.NewPluginHelpers(ldr, v, l.rf, l.pc), yaml)
if err != nil {
return nil, errors.Wrapf(
err, "plugin %s fails configuration", res.OrgId())
@ -176,10 +221,13 @@ func (l *Loader) loadPlugin(res *resource.Resource) (resmap.Configurable, error)
}
func (l *Loader) loadExecOrGoPlugin(resId resid.ResId) (resmap.Configurable, error) {
absPluginPath, err := l.AbsolutePluginPath(resId)
if err != nil {
return nil, err
}
// First try to load the plugin as an executable.
p := execplugin.NewExecPlugin(l.absolutePluginPath(resId))
err := p.ErrIfNotExecutable()
if err == nil {
p := execplugin.NewExecPlugin(absPluginPath)
if err = p.ErrIfNotExecutable(); err == nil {
return p, nil
}
if !os.IsNotExist(err) {
@ -193,7 +241,7 @@ func (l *Loader) loadExecOrGoPlugin(resId resid.ResId) (resmap.Configurable, err
return nil, err
}
// Failing the above, try loading it as a Go plugin.
c, err := l.loadGoPlugin(resId)
c, err := l.loadGoPlugin(resId, absPluginPath+".so")
if err != nil {
return nil, err
}
@ -208,12 +256,11 @@ func (l *Loader) loadExecOrGoPlugin(resId resid.ResId) (resmap.Configurable, err
// as a Loader instance variable. So make it a package variable.
var registry = make(map[string]resmap.Configurable)
func (l *Loader) loadGoPlugin(id resid.ResId) (resmap.Configurable, error) {
func (l *Loader) loadGoPlugin(id resid.ResId, absPath string) (resmap.Configurable, error) {
regId := relativePluginPath(id)
if c, ok := registry[regId]; ok {
return copyPlugin(c), nil
}
absPath := l.absolutePluginPath(id) + ".so"
if !utils.FileExists(absPath) {
return nil, fmt.Errorf(
"expected file with Go object code at: %s", absPath)

View File

@ -34,7 +34,7 @@ func GoBin() string {
// has her ${g}/${v}/$lower(${k})/${k}.go files.
func DeterminePluginSrcRoot(fSys filesys.FileSystem) (string, error) {
return konfig.FirstDirThatExistsElseError(
"source directory", fSys, []konfig.NotedFunc{
"plugin src root", fSys, []konfig.NotedFunc{
{
Note: "relative to unit test",
F: func() string {

View File

@ -443,7 +443,10 @@ func (kt *KustTarget) configureBuiltinPlugin(
err, "builtin %s marshal", bpt)
}
}
err = p.Config(resmap.NewPluginHelpers(kt.ldr, kt.validator, kt.rFactory), y)
err = p.Config(
resmap.NewPluginHelpers(
kt.ldr, kt.validator, kt.rFactory, kt.pLdr.Config()),
y)
if err != nil {
return errors.Wrapf(
err, "trouble configuring builtin %s with config: `\n%s`", bpt, string(y))

View File

@ -112,16 +112,22 @@ var generatorConfigurators = map[builtinhelpers.BuiltinPluginType]func(
return
},
builtinhelpers.HelmChartInflationGenerator: func(kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) (
builtinhelpers.HelmChartInflationGenerator: func(
kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) (
result []resmap.Generator, err error) {
var c struct {
types.HelmChartArgs
types.HelmGlobals
types.HelmChart
}
for _, args := range kt.kustomization.HelmChartInflationGenerator {
c.HelmChartArgs = args
var globals types.HelmGlobals
if kt.kustomization.HelmGlobals != nil {
globals = *kt.kustomization.HelmGlobals
}
for _, chart := range kt.kustomization.HelmCharts {
c.HelmGlobals = globals
c.HelmChart = chart
p := f()
err := kt.configureBuiltinPlugin(p, c, bpt)
if err != nil {
if err = kt.configureBuiltinPlugin(p, c, bpt); err != nil {
return nil, err
}
result = append(result, p)
@ -201,14 +207,16 @@ var transformerConfigurators = map[builtinhelpers.BuiltinPluginType]func(
return
}
var c struct {
Path string `json:"path,omitempty" yaml:"path,omitempty"`
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"`
Path string `json:"path,omitempty" yaml:"path,omitempty"`
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"`
Options map[string]bool `json:"options,omitempty" yaml:"options,omitempty"`
}
for _, pc := range kt.kustomization.Patches {
c.Target = pc.Target
c.Patch = pc.Patch
c.Path = pc.Path
c.Options = pc.Options
p := f()
err = kt.configureBuiltinPlugin(p, c, bpt)
if err != nil {
@ -221,6 +229,31 @@ var transformerConfigurators = map[builtinhelpers.BuiltinPluginType]func(
builtinhelpers.LabelTransformer: func(
kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) (
result []resmap.Transformer, err error) {
for _, label := range kt.kustomization.Labels {
var c struct {
Labels map[string]string
FieldSpecs []types.FieldSpec
}
c.Labels = label.Pairs
fss := types.FsSlice(label.FieldSpecs)
// merge the custom fieldSpecs with the default
if label.IncludeSelectors {
fss, err = fss.MergeAll(tc.CommonLabels)
} else {
// only add to metadata by default
fss, err = fss.MergeOne(types.FieldSpec{Path: "metadata/labels", CreateIfNotPresent: true})
}
if err != nil {
return nil, err
}
c.FieldSpecs = fss
p := f()
err = kt.configureBuiltinPlugin(p, c, bpt)
if err != nil {
return nil, err
}
result = append(result, p)
}
var c struct {
Labels map[string]string
FieldSpecs []types.FieldSpec

View File

@ -4,15 +4,12 @@
package target
import (
"fmt"
"sigs.k8s.io/kustomize/api/resmap"
)
// multiTransformer contains a list of transformers.
type multiTransformer struct {
transformers []resmap.Transformer
checkConflictEnabled bool
transformers []resmap.Transformer
}
var _ resmap.Transformer = &multiTransformer{}
@ -20,8 +17,8 @@ var _ resmap.Transformer = &multiTransformer{}
// newMultiTransformer constructs a multiTransformer.
func newMultiTransformer(t []resmap.Transformer) resmap.Transformer {
r := &multiTransformer{
transformers: make([]resmap.Transformer, len(t)),
checkConflictEnabled: false}
transformers: make([]resmap.Transformer, len(t)),
}
copy(r.transformers, t)
return r
}
@ -29,57 +26,11 @@ func newMultiTransformer(t []resmap.Transformer) resmap.Transformer {
// Transform applies the member transformers in order to the resources,
// optionally detecting and erroring on commutation conflict.
func (o *multiTransformer) Transform(m resmap.ResMap) error {
if o.checkConflictEnabled {
return o.transformWithCheckConflict(m)
}
return o.transform(m)
}
func (o *multiTransformer) transform(m resmap.ResMap) error {
for _, t := range o.transformers {
err := t.Transform(m)
if err != nil {
if err := t.Transform(m); err != nil {
return err
}
}
for _, r := range m.Resources() {
empty, err := r.IsEmpty()
if err != nil {
return err
}
if empty {
err := m.Remove(r.CurId())
if err != nil {
return err
}
}
m.DropEmpties()
}
return nil
}
// Of the len(o.transformers)! possible transformer orderings, compare to a reversed order.
// A spot check to perform when the transformations are supposed to be commutative.
// Fail if there's a difference in the result.
func (o *multiTransformer) transformWithCheckConflict(m resmap.ResMap) error {
mcopy := m.DeepCopy()
err := o.transform(m)
if err != nil {
return err
}
o.reverseTransformers()
err = o.transform(mcopy)
if err != nil {
return err
}
err = m.ErrorIfNotEqualSets(mcopy)
if err != nil {
return fmt.Errorf("found conflict between different patches\n%v", err)
}
return nil
}
func (o *multiTransformer) reverseTransformers() {
for i, j := 0, len(o.transformers)-1; i < j; i, j = i+1, j-1 {
o.transformers[i], o.transformers[j] = o.transformers[j], o.transformers[i]
}
}

View File

@ -1,108 +0,0 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package wrappy
import (
"fmt"
"sigs.k8s.io/kustomize/api/hasher"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/generators"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// WNodeFactory makes instances of WNode.
//
// These instances in turn adapt
// sigs.k8s.io/kustomize/kyaml/yaml.RNode
// to implement ifc.Unstructured.
// This factory is meant to implement ifc.KunstructuredFactory.
//
// This implementation should be thin, as both WNode and WNodeFactory must be
// factored away (deleted) along with ifc.Kunstructured in favor of direct use
// of RNode methods upon completion of
// https://github.com/kubernetes-sigs/kustomize/issues/2506.
//
// See also api/krusty/internal/provider/depprovider.go
type WNodeFactory struct {
}
var _ ifc.KunstructuredFactory = (*WNodeFactory)(nil)
func (k *WNodeFactory) SliceFromBytes(bs []byte) ([]ifc.Kunstructured, error) {
yamlRNodes, err := kio.FromBytes(bs)
if err != nil {
return nil, err
}
var result []ifc.Kunstructured
for i := range yamlRNodes {
rn := yamlRNodes[i]
meta, err := rn.GetValidatedMetadata()
if err != nil {
return nil, err
}
if !shouldDropObject(meta) {
if foundNil, path := rn.HasNilEntryInList(); foundNil {
return nil, fmt.Errorf("empty item at %v in object %v", path, rn)
}
result = append(result, FromRNode(rn))
}
}
return result, nil
}
// shouldDropObject returns true if the resource should not be accumulated.
func shouldDropObject(m yaml.ResourceMeta) bool {
_, y := m.ObjectMeta.Annotations[konfig.IgnoredByKustomizeAnnotation]
return y
}
func (k *WNodeFactory) FromMap(m map[string]interface{}) ifc.Kunstructured {
rn, err := FromMap(m)
if err != nil {
// TODO(#WNodeFactory): handle or bubble error"
panic(err)
}
return rn
}
// kustHash computes a hash of an unstructured object.
type kustHash struct{}
// Hash returns a hash of the given object
func (h *kustHash) Hash(m ifc.Kunstructured) (string, error) {
node, err := filtersutil.GetRNode(m)
if err != nil {
return "", err
}
return hasher.HashRNode(node)
}
func (k *WNodeFactory) Hasher() ifc.KunstructuredHasher {
return &kustHash{}
}
// MakeConfigMap makes a wrapped configmap.
func (k *WNodeFactory) MakeConfigMap(
ldr ifc.KvLoader, args *types.ConfigMapArgs) (ifc.Kunstructured, error) {
rn, err := generators.MakeConfigMap(ldr, args)
if err != nil {
return nil, err
}
return FromRNode(rn), nil
}
// MakeSecret makes a wrapped secret.
func (k *WNodeFactory) MakeSecret(
ldr ifc.KvLoader, args *types.SecretArgs) (ifc.Kunstructured, error) {
rn, err := generators.MakeSecret(ldr, args)
if err != nil {
return nil, err
}
return FromRNode(rn), nil
}

View File

@ -1,292 +0,0 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package wrappy
import (
"fmt"
"log"
"regexp"
"strconv"
"strings"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// WNode implements ifc.Kunstructured using yaml.RNode.
//
// It exists only to help manage a switch from
// kunstruct.UnstructAdapter to yaml.RNode as the core
// representation of KRM objects in kustomize.
//
// It's got a silly name because we don't want it around for long,
// and want its use to be obvious.
type WNode struct {
node *yaml.RNode
}
var _ ifc.Kunstructured = (*WNode)(nil)
func NewWNode() *WNode {
return FromRNode(yaml.NewRNode(nil))
}
func FromMap(m map[string]interface{}) (*WNode, error) {
n, err := yaml.FromMap(m)
if err != nil {
return nil, err
}
return FromRNode(n), nil
}
func FromRNode(node *yaml.RNode) *WNode {
return &WNode{node: node}
}
func (wn *WNode) AsRNode() *yaml.RNode {
return wn.node
}
func (wn *WNode) demandMetaData(label string) yaml.ResourceMeta {
meta, err := wn.node.GetMeta()
if err != nil {
// Log and die since interface doesn't allow error.
log.Fatalf("for %s', expected valid resource: %v", label, err)
}
return meta
}
// Copy implements ifc.Kunstructured.
func (wn *WNode) Copy() ifc.Kunstructured {
return &WNode{node: wn.node.Copy()}
}
// GetAnnotations implements ifc.Kunstructured.
func (wn *WNode) GetAnnotations() map[string]string {
return wn.demandMetaData("GetAnnotations").Annotations
}
// convertSliceIndex traverses the items in `fields` and find
// if there is a slice index in the item and change it to a
// valid Lookup field path. For example, 'ports[0]' will be
// converted to 'ports' and '0'.
func convertSliceIndex(fields []string) []string {
var res []string
for _, s := range fields {
if !strings.HasSuffix(s, "]") {
res = append(res, s)
continue
}
re := regexp.MustCompile(`^(.*)\[(\d+)\]$`)
groups := re.FindStringSubmatch(s)
if len(groups) == 0 {
// no match, add to result
res = append(res, s)
continue
}
if groups[1] != "" {
res = append(res, groups[1])
}
res = append(res, groups[2])
}
return res
}
// GetFieldValue implements ifc.Kunstructured.
func (wn *WNode) GetFieldValue(path string) (interface{}, error) {
fields := convertSliceIndex(strings.Split(path, "."))
rn, err := wn.node.Pipe(yaml.Lookup(fields...))
if err != nil {
return nil, err
}
if rn == nil {
return nil, NoFieldError{path}
}
yn := rn.YNode()
// If this is an alias node, resolve it
if yn.Kind == yaml.AliasNode {
yn = yn.Alias
}
// Return value as map for DocumentNode and MappingNode kinds
if yn.Kind == yaml.DocumentNode || yn.Kind == yaml.MappingNode {
var result map[string]interface{}
if err := yn.Decode(&result); err != nil {
return nil, err
}
return result, err
}
// Return value as slice for SequenceNode kind
if yn.Kind == yaml.SequenceNode {
var result []interface{}
if err := yn.Decode(&result); err != nil {
return nil, err
}
return result, nil
}
if yn.Kind != yaml.ScalarNode {
return nil, fmt.Errorf("expected ScalarNode, got Kind=%d", yn.Kind)
}
// TODO: When doing kustomize var replacement, which is likely a
// a primary use of this function and the reason it returns interface{}
// rather than string, we do conversion from Nodes to Go types and back
// to nodes. We should figure out how to do replacement using raw nodes,
// assuming we keep the var feature in kustomize.
// The other end of this is: refvar.go:updateNodeValue.
switch yn.Tag {
case yaml.NodeTagString:
return yn.Value, nil
case yaml.NodeTagInt:
return strconv.Atoi(yn.Value)
case yaml.NodeTagFloat:
return strconv.ParseFloat(yn.Value, 64)
case yaml.NodeTagBool:
return strconv.ParseBool(yn.Value)
default:
// Possibly this should be an error or log.
return yn.Value, nil
}
}
// GetGvk implements ifc.Kunstructured.
func (wn *WNode) GetGvk() resid.Gvk {
meta := wn.demandMetaData("GetGvk")
g, v := resid.ParseGroupVersion(meta.APIVersion)
return resid.Gvk{Group: g, Version: v, Kind: meta.Kind}
}
// GetDataMap implements ifc.Kunstructured.
func (wn *WNode) GetDataMap() map[string]string {
return wn.node.GetDataMap()
}
// SetDataMap implements ifc.Kunstructured.
func (wn *WNode) SetDataMap(m map[string]string) {
wn.node.SetDataMap(m)
}
// GetBinaryDataMap implements ifc.Kunstructured.
func (wn *WNode) GetBinaryDataMap() map[string]string {
return wn.node.GetBinaryDataMap()
}
// SetBinaryDataMap implements ifc.Kunstructured.
func (wn *WNode) SetBinaryDataMap(m map[string]string) {
wn.node.SetBinaryDataMap(m)
}
// GetKind implements ifc.Kunstructured.
func (wn *WNode) GetKind() string {
return wn.demandMetaData("GetKind").Kind
}
// GetLabels implements ifc.Kunstructured.
func (wn *WNode) GetLabels() map[string]string {
return wn.demandMetaData("GetLabels").Labels
}
// GetName implements ifc.Kunstructured.
func (wn *WNode) GetName() string {
return wn.demandMetaData("GetName").Name
}
// GetSlice implements ifc.Kunstructured.
func (wn *WNode) GetSlice(path string) ([]interface{}, error) {
value, err := wn.GetFieldValue(path)
if err != nil {
return nil, err
}
if sliceValue, ok := value.([]interface{}); ok {
return sliceValue, nil
}
return nil, fmt.Errorf("node %s is not a slice", path)
}
// GetSlice implements ifc.Kunstructured.
func (wn *WNode) GetString(path string) (string, error) {
value, err := wn.GetFieldValue(path)
if err != nil {
return "", err
}
if v, ok := value.(string); ok {
return v, nil
}
return "", fmt.Errorf("node %s is not a string: %v", path, value)
}
// Map implements ifc.Kunstructured.
func (wn *WNode) Map() (map[string]interface{}, error) {
return wn.node.Map()
}
// MarshalJSON implements ifc.Kunstructured.
func (wn *WNode) MarshalJSON() ([]byte, error) {
return wn.node.MarshalJSON()
}
// MatchesAnnotationSelector implements ifc.Kunstructured.
func (wn *WNode) MatchesAnnotationSelector(selector string) (bool, error) {
return wn.node.MatchesAnnotationSelector(selector)
}
// MatchesLabelSelector implements ifc.Kunstructured.
func (wn *WNode) MatchesLabelSelector(selector string) (bool, error) {
return wn.node.MatchesLabelSelector(selector)
}
// SetAnnotations implements ifc.Kunstructured.
func (wn *WNode) SetAnnotations(annotations map[string]string) {
if err := wn.node.SetAnnotations(annotations); err != nil {
log.Fatal(err) // interface doesn't allow error.
}
}
// SetGvk implements ifc.Kunstructured.
func (wn *WNode) SetGvk(gvk resid.Gvk) {
wn.setMapField(yaml.NewScalarRNode(gvk.Kind), yaml.KindField)
wn.setMapField(yaml.NewScalarRNode(gvk.ApiVersion()), yaml.APIVersionField)
}
// SetLabels implements ifc.Kunstructured.
func (wn *WNode) SetLabels(labels map[string]string) {
if err := wn.node.SetLabels(labels); err != nil {
log.Fatal(err) // interface doesn't allow error.
}
}
// SetName implements ifc.Kunstructured.
func (wn *WNode) SetName(name string) {
wn.setMapField(yaml.NewScalarRNode(name), yaml.MetadataField, yaml.NameField)
}
// SetNamespace implements ifc.Kunstructured.
func (wn *WNode) SetNamespace(ns string) {
if err := wn.node.SetNamespace(ns); err != nil {
log.Fatal(err) // interface doesn't allow error.
}
}
func (wn *WNode) setMapField(value *yaml.RNode, path ...string) {
if err := wn.node.SetMapField(value, path...); err != nil {
// Log and die since interface doesn't allow error.
log.Fatalf("failed to set field %v: %v", path, err)
}
}
// UnmarshalJSON implements ifc.Kunstructured.
func (wn *WNode) UnmarshalJSON(data []byte) error {
return wn.node.UnmarshalJSON(data)
}
type NoFieldError struct {
Field string
}
func (e NoFieldError) Error() string {
return fmt.Sprintf("no field named '%s'", e.Field)
}

View File

@ -19,5 +19,8 @@ namespace:
group: apiregistration.k8s.io
kind: APIService
create: true
- path: spec/conversion/webhook/clientConfig/service/namespace
group: apiextensions.k8s.io
kind: CustomResourceDefinition
`
)

View File

@ -19,31 +19,7 @@ func DefaultKustomizationFileName() string {
return RecognizedKustomizationFileNames()[0]
}
// IfApiMachineryElseKyaml returns true if executing the apimachinery code
// path, else we're executing the kyaml code paths.
func IfApiMachineryElseKyaml(s1, s2 string) string {
if !FlagEnableKyamlDefaultValue {
return s1
}
return s2
}
const (
// FlagEnableKyamlDefaultValue is the default value for the --enable_kyaml
// flag. This value is also used in unit tests. See provider.DepProvider.
//
// TODO(#3588): Delete this constant.
//
// All tests should pass for either true or false values
// of this constant, without having to check its value.
// In the cases where there's a different outcome, either decide
// that the difference is acceptable, or make the difference go away.
//
// Historically, tests passed for enable_kyaml == false, i.e. using
// apimachinery libs. This doesn't mean the code was better, it just
// means regression tests preserved those outcomes.
FlagEnableKyamlDefaultValue = true
// An environment variable to consult for kustomization
// configuration data. See:
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html

View File

@ -41,32 +41,6 @@ const (
NoPluginHomeSentinal = "/No/non-builtin/plugins!"
)
func EnabledPluginConfig(b types.BuiltinPluginLoadingOptions) (*types.PluginConfig, error) {
dir, err := DefaultAbsPluginHome(filesys.MakeFsOnDisk())
if err != nil {
return nil, err
}
return MakePluginConfig(types.PluginRestrictionsNone, b, dir), nil
}
func DisabledPluginConfig() *types.PluginConfig {
return MakePluginConfig(
types.PluginRestrictionsBuiltinsOnly,
types.BploUseStaticallyLinked,
NoPluginHomeSentinal)
}
func MakePluginConfig(
pr types.PluginRestrictions,
b types.BuiltinPluginLoadingOptions,
home string) *types.PluginConfig {
return &types.PluginConfig{
PluginRestrictions: pr,
AbsPluginHome: home,
BpLoadingOptions: b,
}
}
type NotedFunc struct {
Note string
F func() string
@ -77,7 +51,7 @@ type NotedFunc struct {
// the home of kustomize plugins.
func DefaultAbsPluginHome(fSys filesys.FileSystem) (string, error) {
return FirstDirThatExistsElseError(
"plugin home directory", fSys, []NotedFunc{
"plugin root", fSys, []NotedFunc{
{
Note: "homed in $" + KustomizePluginHomeEnv,
F: func() string {
@ -87,9 +61,11 @@ func DefaultAbsPluginHome(fSys filesys.FileSystem) (string, error) {
{
Note: "homed in $" + XdgConfigHomeEnv,
F: func() string {
return filepath.Join(
os.Getenv(XdgConfigHomeEnv),
ProgramName, RelPluginHome)
if root := os.Getenv(XdgConfigHomeEnv); root != "" {
return filepath.Join(root, ProgramName, RelPluginHome)
}
// do not look in "kustomize/plugin" if XdgConfigHomeEnv is unset
return ""
},
},
{
@ -118,11 +94,14 @@ func FirstDirThatExistsElseError(
pathFuncs []NotedFunc) (string, error) {
var nope []types.Pair
for _, dt := range pathFuncs {
dir := dt.F()
if fSys.Exists(dir) {
return dir, nil
if dir := dt.F(); dir != "" {
if fSys.Exists(dir) {
return dir, nil
}
nope = append(nope, types.Pair{Key: dt.Note, Value: dir})
} else {
nope = append(nope, types.Pair{Key: dt.Note, Value: "<no value>"})
}
nope = append(nope, types.Pair{Key: dt.Note, Value: dir})
}
return "", types.NewErrUnableToFind(what, nope)
}

View File

@ -36,7 +36,7 @@ type Kustomizer struct {
func MakeKustomizer(o *Options) *Kustomizer {
return &Kustomizer{
options: o,
depProvider: provider.NewDepProvider(o.UseKyaml),
depProvider: provider.NewDepProvider(),
}
}
@ -52,9 +52,7 @@ func MakeKustomizer(o *Options) *Kustomizer {
// and Run can be called on each of them).
func (b *Kustomizer) Run(
fSys filesys.FileSystem, path string) (resmap.ResMap, error) {
resmapFactory := resmap.NewFactory(
b.depProvider.GetResourceFactory(),
b.depProvider.GetConflictDetectorFactory())
resmapFactory := resmap.NewFactory(b.depProvider.GetResourceFactory())
lr := fLdr.RestrictionNone
if b.options.LoadRestrictions == types.LoadRestrictionsRootOnly {
lr = fLdr.RestrictionRootOnly
@ -68,7 +66,8 @@ func (b *Kustomizer) Run(
ldr,
b.depProvider.GetFieldValidator(),
resmapFactory,
pLdr.NewLoader(b.options.PluginConfig, resmapFactory),
// The plugin configs are always located on disk, regardless of the fSys passed in
pLdr.NewLoader(b.options.PluginConfig, resmapFactory, filesys.MakeFsOnDisk()),
)
err = kt.Load()
if err != nil {

View File

@ -5,7 +5,6 @@ package krusty
import (
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/types"
)
@ -33,37 +32,19 @@ type Options struct {
// Options related to kustomize plugins.
PluginConfig *types.PluginConfig
// TODO(#3588): Delete this field (it's always true).
// When true, use kyaml/ packages to manipulate KRM yaml.
// When false, use k8sdeps/ instead (uses k8s.io/api* packages).
UseKyaml bool
// When true, allow name and kind changing via a patch
// When false, patch name/kind don't overwrite target name/kind
AllowResourceIdChanges bool
}
// MakeDefaultOptions returns a default instance of Options.
func MakeDefaultOptions() *Options {
return &Options{
DoLegacyResourceSort: false,
AddManagedbyLabel: false,
LoadRestrictions: types.LoadRestrictionsRootOnly,
DoPrune: false,
PluginConfig: konfig.DisabledPluginConfig(),
UseKyaml: konfig.FlagEnableKyamlDefaultValue,
AllowResourceIdChanges: false,
DoLegacyResourceSort: false,
AddManagedbyLabel: false,
LoadRestrictions: types.LoadRestrictionsRootOnly,
DoPrune: false,
PluginConfig: types.DisabledPluginConfig(),
}
}
func (o Options) IfApiMachineryElseKyaml(s1, s2 string) string {
if !o.UseKyaml {
return s1
}
return s2
}
// GetBuiltinPluginNames returns a list of builtin plugin names
func GetBuiltinPluginNames() []string {
var ret []string

View File

@ -319,7 +319,6 @@ func (fl *fileLoader) Load(path string) ([]byte, error) {
}
return body, nil
}
if !filepath.IsAbs(path) {
path = fl.root.Join(path)
}

View File

@ -4,198 +4,39 @@
package provider
import (
"log"
"sigs.k8s.io/kustomize/api/hasher"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/conflict"
"sigs.k8s.io/kustomize/api/internal/validate"
"sigs.k8s.io/kustomize/api/internal/wrappy"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/resource"
)
// DepProvider is a dependency provider.
//
// The instances it returns are either
// - old implementations backed by k8sdeps code,
// - new implementations backed by kyaml code.
//
// History:
//
// kubectl depends on k8s.io code, and at the time of writing, so
// does kustomize. Code that imports k8s.io/api* cannot be imported
// back into k8s.io/*, yet kustomize appears inside k8s.io/kubectl.
//
// To allow kustomize to appear inside kubectl, yet still be developed
// outside kubectl, the kustomize code was divided into the following
// packages
//
// api/
// k8sdeps/ (and internal/ks8deps/)
// ifc/
// krusty/
// everythingElse/
//
// with the following rules:
//
// - Only k8sdeps/ may import k8s.io/api*.
//
// - Only krusty/ (and its internals) may import k8sdeps/.
// I.e., ifc/ and everythingElse/ must not
// import k8sdeps/ or k8s.io/api*.
//
// - Code in krusty/ may use code in k8sdeps/ to create
// objects then inject said objects into
// everythingElse/ behind dependency neutral interfaces.
//
// The idea was to periodically copy, not import, the large k8sdeps/
// tree (plus a snippet from krusty/kustomizer.go) into the kubectl
// codebase via a large PR, and have kubectl depend on the rest via
// normal importing.
//
// Over 2019, however, kubectl underwent large changes including
// a switch to Go modules, and a concerted attempt to extract kubectl
// from the k8s repo. This made large kustomize integration PRs too
// intrusive to review.
//
// In 2020, kubectl is based on Go modules, and almost entirely
// extracted from the k8s.io repositories, and further the kyaml
// library has a appeared as a viable replacement to k8s.io/api*
// KRM manipulation code.
//
// The new plan is to eliminate k8sdeps/ entirely, along with its
// k8s.io/api* dependence, allowing kustomize code to be imported
// into kubectl via normal Go module imports. Then the kustomize API
// code can then move into the github.com/kubernetes-sigs/cli-utils
// repo. The kustomize CLI in github.com/kubernetes-sigs/kustomize
// and the kubectl CLI can then both depend on the kustomize API.
//
// So, all code that depends on k8sdeps must go behind interfaces,
// and kustomize must be factored to choose the implementation.
//
// That problem has been reduced to three interfaces, each having
// two implementations. (1) is k8sdeps-based, (2) is kyaml-based.
//
// - ifc.Kunstructured
//
// 1) api/k8sdeps/kunstruct.UnstructAdapter
//
// This adapts structs in
// k8s.io/apimachinery/pkg/apis/meta/v1/unstructured
// to ifc.Kunstructured.
//
// 2) api/wrappy.WNode
//
// This adapts sigs.k8s.io/kustomize/kyaml/yaml.RNode
// to ifc.Unstructured.
//
// At time of writing, implementation started.
// Further reducing the size of ifc.Kunstructed
// would really reduce the work
// (e.g. drop Vars, drop ReplacementTranformer).
//
// - resource.ConflictDetector
//
// 1) api/internal/k8sdeps/conflict.conflictDetectorJson
// api/internal/k8sdeps/conflict.conflictDetectorSm
//
// Uses k8s.io/apimachinery/pkg/util/strategicpatch,
// apimachinery/pkg/util/mergepatch, etc. to merge
// resource.Resource instances.
//
// 2) api/internal/conflict.smPatchMergeOnlyDetector
//
// At time of writing, this doesn't report conflicts,
// but it does know how to merge patches. Conflict
// reporting isn't vital to kustomize function. It's
// rare that a person would configure one transformer
// with many patches, much less so many that it became
// hard to spot conflicts. In the case of an undetected
// conflict, the last patch applied wins, likely what
// the user wants anyway. Regardless, the effect of this
// is plainly visible and usable in the output, even if
// a conflict happened but wasn't reported as an error.
//
// - ifc.Validator
//
// 1) api/k8sdeps/validator.KustValidator
//
// Uses k8s.io/apimachinery/pkg/api/validation and
// friends to validate strings.
//
// 2) api/internal/validate.FieldValidator
//
// See TODO inside the validator for status.
// At time of writing, this is a do-nothing
// validator as it's not critical to kustomize function.
//
// Proposed plan:
// [x] Ship kustomize with the ability to switch from 1 to 2 via
// an --enable_kyaml flag.
// [x] Make --enable_kyaml true by default.
// [x] When 2 is not noticeably more buggy than 1, delete 1.
// I.e. delete k8sdeps/, transitively deleting all k8s.io/api* deps.
// This DepProvider should be left in place to retain these
// comments, but it will have only one choice.
// [x] The way is now clear to reintegrate into kubectl.
// This should be done ASAP; the last step is cleanup.
// [ ] Cleanup. With only one impl of Kunstructure remaining,
// that interface and WNode can be deleted, along with this
// DepProvider. The other two interfaces could be dropped too.
//
// When the above is done, kustomize will use yaml.RNode and/or
// KRM Config Functions directly and exclusively.
// If you're reading this, plan not done.
//
// DepProvider is a dependency provider, injecting different
// implementations depending on the context.
type DepProvider struct {
kFactory ifc.KunstructuredFactory
resourceFactory *resource.Factory
conflictDectectorFactory resource.ConflictDetectorFactory
fieldValidator ifc.Validator
resourceFactory *resource.Factory
// implemented by api/internal/validate.FieldValidator
// See TODO inside the validator for status.
// At time of writing, this is a do-nothing
// validator as it's not critical to kustomize function.
fieldValidator ifc.Validator
}
// The dependencies this method needs have been deleted -
// see comments above. This method will be deleted
// along with DepProvider in the final step.
func makeK8sdepBasedInstances() *DepProvider {
log.Fatal("This binary cannot use k8s.io code; it must use kyaml.")
return nil
}
func makeKyamlBasedInstances() *DepProvider {
kf := &wrappy.WNodeFactory{}
rf := resource.NewFactory(kf)
func NewDepProvider() *DepProvider {
rf := resource.NewFactory(&hasher.Hasher{})
return &DepProvider{
kFactory: kf,
resourceFactory: rf,
conflictDectectorFactory: conflict.NewFactory(),
fieldValidator: validate.NewFieldValidator(),
resourceFactory: rf,
fieldValidator: validate.NewFieldValidator(),
}
}
func NewDepProvider(useKyaml bool) *DepProvider {
if useKyaml {
return makeKyamlBasedInstances()
}
return makeK8sdepBasedInstances()
}
func NewDefaultDepProvider() *DepProvider {
return NewDepProvider(konfig.FlagEnableKyamlDefaultValue)
}
func (dp *DepProvider) GetKunstructuredFactory() ifc.KunstructuredFactory {
return dp.kFactory
return NewDepProvider()
}
func (dp *DepProvider) GetResourceFactory() *resource.Factory {
return dp.resourceFactory
}
func (dp *DepProvider) GetConflictDetectorFactory() resource.ConflictDetectorFactory {
return dp.conflictDectectorFactory
}
func (dp *DepProvider) GetFieldValidator() ifc.Validator {
return dp.fieldValidator
}

View File

@ -36,6 +36,14 @@ func ParseGroupVersion(apiVersion string) (group, version string) {
// GvkFromString makes a Gvk from the output of Gvk.String().
func GvkFromString(s string) Gvk {
values := strings.Split(s, fieldSep)
if len(values) != 3 {
// ...then the string didn't come from Gvk.String().
return Gvk{
Group: noGroup,
Version: noVersion,
Kind: noKind,
}
}
g := values[0]
if g == noGroup {
g = ""
@ -213,7 +221,10 @@ func (x Gvk) toKyamlTypeMeta() yaml.TypeMeta {
}
}
// IsNamespaceableKind returns true if x is a namespaceable Gvk
// IsNamespaceableKind returns true if x is a namespaceable Gvk,
// e.g. instances of Pod and Deployment are namespaceable,
// but instances of Node and Namespace are not namespaceable.
// Alternative name for this method: IsNotClusterScoped.
// Implements https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/#not-all-objects-are-in-a-namespace
func (x Gvk) IsNamespaceableKind() bool {
isNamespaceScoped, found := openapi.IsNamespaceScoped(x.toKyamlTypeMeta())

View File

@ -16,14 +16,11 @@ import (
type Factory struct {
// Makes resources.
resF *resource.Factory
// Makes ConflictDetectors.
cdf resource.ConflictDetectorFactory
}
// NewFactory returns a new resmap.Factory.
func NewFactory(
rf *resource.Factory, cdf resource.ConflictDetectorFactory) *Factory {
return &Factory{resF: rf, cdf: cdf}
func NewFactory(rf *resource.Factory) *Factory {
return &Factory{resF: rf}
}
// RF returns a resource.Factory.
@ -126,13 +123,6 @@ func (rmF *Factory) FromSecretArgs(
return rmF.FromResource(res), nil
}
// ConflatePatches creates a new ResMap containing a merger of the
// incoming patches.
// Error if conflict found.
func (rmF *Factory) ConflatePatches(patches []*resource.Resource) (ResMap, error) {
return (&merginator{cdf: rmF.cdf}).ConflatePatches(patches)
}
func newResMapFromResourceSlice(
resources []*resource.Resource) (ResMap, error) {
result := New()
@ -146,18 +136,10 @@ func newResMapFromResourceSlice(
}
// NewResMapFromRNodeSlice returns a ResMap from a slice of RNodes
func (rmF *Factory) NewResMapFromRNodeSlice(rnodes []*yaml.RNode) (ResMap, error) {
var resources []*resource.Resource
for _, rnode := range rnodes {
s, err := rnode.String()
if err != nil {
return nil, err
}
r, err := rmF.resF.SliceFromBytes([]byte(s))
if err != nil {
return nil, err
}
resources = append(resources, r...)
func (rmF *Factory) NewResMapFromRNodeSlice(s []*yaml.RNode) (ResMap, error) {
rs, err := rmF.resF.ResourcesFromRNodes(s)
if err != nil {
return nil, err
}
return newResMapFromResourceSlice(resources)
return newResMapFromResourceSlice(rs)
}

View File

@ -1,123 +0,0 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package resmap
import (
"fmt"
"sigs.k8s.io/kustomize/api/resource"
)
// merginator coordinates merging the resources in incoming to the result.
type merginator struct {
incoming []*resource.Resource
cdf resource.ConflictDetectorFactory
result ResMap
}
func (m *merginator) ConflatePatches(in []*resource.Resource) (ResMap, error) {
m.result = New()
m.incoming = in
for index := range m.incoming {
alreadyInResult, err := m.appendIfNoMatch(index)
if err != nil {
return nil, err
}
if alreadyInResult != nil {
// The resource at index has the same resId as a previously
// considered resource.
//
// If they conflict with each other (e.g. they both want to change
// the image name in a Deployment, but to different values),
// return an error.
//
// If they don't conflict, then merge them into a single resource,
// since they both target the same item, and we want cumulative
// behavior. E.g. say both patches modify a map. Without a merge,
// the last patch wins, replacing the entire map.
err = m.mergeWithExisting(index, alreadyInResult)
if err != nil {
return nil, err
}
}
}
return m.result, nil
}
func (m *merginator) appendIfNoMatch(index int) (*resource.Resource, error) {
candidate := m.incoming[index]
matchedResources := m.result.GetMatchingResourcesByAnyId(
candidate.OrgId().Equals)
if len(matchedResources) == 0 {
m.result.Append(candidate)
return nil, nil
}
if len(matchedResources) > 1 {
return nil, fmt.Errorf("multiple resources targeted by patch")
}
return matchedResources[0], nil
}
func (m *merginator) mergeWithExisting(
index int, alreadyInResult *resource.Resource) error {
candidate := m.incoming[index]
cd, err := m.cdf.New(candidate.OrgId().Gvk)
if err != nil {
return err
}
hasConflict, err := cd.HasConflict(candidate, alreadyInResult)
if err != nil {
return err
}
if hasConflict {
return m.makeError(cd, index)
}
merged, err := cd.MergePatches(alreadyInResult, candidate)
if err != nil {
return err
}
_, err = m.result.Replace(merged)
return err
}
// Make an error message describing the conflict.
func (m *merginator) makeError(cd resource.ConflictDetector, index int) error {
conflict, err := m.findConflict(cd, index)
if err != nil {
return err
}
if conflict == nil {
return fmt.Errorf("expected conflict for %s", m.incoming[index].OrgId())
}
conflictMap, _ := conflict.Map()
incomingIndexMap, _ := m.incoming[index].Map()
return fmt.Errorf(
"conflict between %#v at index %d and %#v",
incomingIndexMap,
index,
conflictMap,
)
}
// findConflict looks for a conflict in a resource slice.
// It returns the first conflict between the resource at index
// and some other resource. Two resources can only conflict if
// they have the same original ResId.
func (m *merginator) findConflict(
cd resource.ConflictDetector, index int) (*resource.Resource, error) {
targetId := m.incoming[index].OrgId()
for i, p := range m.incoming {
if i == index || !targetId.Equals(p.OrgId()) {
continue
}
conflict, err := cd.HasConflict(p, m.incoming[index])
if err != nil {
return nil, err
}
if conflict {
return p, nil
}
}
return nil, nil
}

View File

@ -33,8 +33,10 @@ type Configurable interface {
}
// NewPluginHelpers makes an instance of PluginHelpers.
func NewPluginHelpers(ldr ifc.Loader, v ifc.Validator, rf *Factory) *PluginHelpers {
return &PluginHelpers{ldr: ldr, v: v, rf: rf}
func NewPluginHelpers(
ldr ifc.Loader, v ifc.Validator, rf *Factory,
pc *types.PluginConfig) *PluginHelpers {
return &PluginHelpers{ldr: ldr, v: v, rf: rf, pc: pc}
}
// PluginHelpers holds things that any or all plugins might need.
@ -44,6 +46,11 @@ type PluginHelpers struct {
ldr ifc.Loader
v ifc.Validator
rf *Factory
pc *types.PluginConfig
}
func (c *PluginHelpers) GeneralConfig() *types.PluginConfig {
return c.pc
}
func (c *PluginHelpers) Loader() ifc.Loader {
@ -80,6 +87,9 @@ type TransformerPlugin interface {
// resource to transform, try the OrgId first, and if this
// fails or finds too many, it might make sense to then try
// the CurrId. Depends on the situation.
//
// TODO: get rid of this interface (use bare resWrangler).
// There aren't multiple implementations any more.
type ResMap interface {
// Size reports the number of resources.
Size() int
@ -189,6 +199,9 @@ type ResMap interface {
// Clear removes all resources and Ids.
Clear()
// DropEmpties drops empty resources from the ResMap.
DropEmpties()
// SubsetThatCouldBeReferencedByResource returns a ResMap subset
// of self with resources that could be referenced by the
// resource argument.
@ -231,9 +244,8 @@ type ResMap interface {
// are selected by a Selector
Select(types.Selector) ([]*resource.Resource, error)
// ToRNodeSlice converts the resources in the resmp
// to a list of RNodes
ToRNodeSlice() ([]*yaml.RNode, error)
// ToRNodeSlice returns a copy of the resources as RNodes.
ToRNodeSlice() []*yaml.RNode
// ApplySmPatch applies a strategic-merge patch to the
// selected set of resources.

View File

@ -6,14 +6,12 @@ package resmap
import (
"bytes"
"fmt"
"strings"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
kyaml_yaml "sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/yaml"
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
)
// resWrangler implements ResMap.
@ -38,6 +36,18 @@ func (m *resWrangler) Clear() {
m.rList = nil
}
// DropEmpties quickly drops empty resources.
// It doesn't use Append, which checks for Id collisions.
func (m *resWrangler) DropEmpties() {
var rList []*resource.Resource
for _, r := range m.rList {
if !r.IsEmpty() {
rList = append(rList, r)
}
}
m.rList = rList
}
// Size implements ResMap.
func (m *resWrangler) Size() int {
return len(m.rList)
@ -66,22 +76,27 @@ func (m *resWrangler) Append(res *resource.Resource) error {
return fmt.Errorf(
"may not add resource with an already registered id: %s", id)
}
m.rList = append(m.rList, res)
m.append(res)
return nil
}
// append appends without performing an Id check
func (m *resWrangler) append(res *resource.Resource) {
m.rList = append(m.rList, res)
}
// Remove implements ResMap.
func (m *resWrangler) Remove(adios resid.ResId) error {
tmp := newOne()
var rList []*resource.Resource
for _, r := range m.rList {
if r.CurId() != adios {
tmp.Append(r)
rList = append(rList, r)
}
}
if tmp.Size() != m.Size()-1 {
if len(rList) != m.Size()-1 {
return fmt.Errorf("id %s not found in removal", adios)
}
m.rList = tmp.rList
m.rList = rList
return nil
}
@ -118,16 +133,7 @@ func (m *resWrangler) Debug(title string) {
} else {
fmt.Println("---")
}
fmt.Printf("# %d %s\n", i, r.OrgId())
m, err := r.Map()
if err != nil {
panic(err)
}
blob, err := yaml.Marshal(m)
if err != nil {
panic(err)
}
fmt.Println(string(blob))
fmt.Printf("# %d %s\n%s\n", i, r.OrgId(), r.String())
}
}
@ -273,7 +279,7 @@ func (m *resWrangler) AsYaml() ([]byte, error) {
firstObj := true
var b []byte
buf := bytes.NewBuffer(b)
for _, res := range m.Resources() {
for _, res := range m.rList {
out, err := res.AsYAML()
if err != nil {
m, _ := res.Map()
@ -297,7 +303,7 @@ func (m *resWrangler) AsYaml() ([]byte, error) {
func (m *resWrangler) ErrorIfNotEqualSets(other ResMap) error {
m2, ok := other.(*resWrangler)
if !ok {
panic("bad cast")
return fmt.Errorf("bad cast to resWrangler 1")
}
if m.Size() != m2.Size() {
return fmt.Errorf(
@ -317,9 +323,9 @@ func (m *resWrangler) ErrorIfNotEqualSets(other ResMap) error {
"id in self matches %d in other; id: %s", len(others), id)
}
r2 := others[0]
if !r1.KunstructEqual(r2) {
if !r1.NodeEqual(r2) {
return fmt.Errorf(
"kunstruct not equal: \n -- %s,\n -- %s\n\n--\n%#v\n------\n%#v\n",
"nodes unequal: \n -- %s,\n -- %s\n\n--\n%#v\n------\n%#v\n",
r1, r2, r1, r2)
}
seen[m2.indexOfResource(r2)] = true
@ -334,7 +340,7 @@ func (m *resWrangler) ErrorIfNotEqualSets(other ResMap) error {
func (m *resWrangler) ErrorIfNotEqualLists(other ResMap) error {
m2, ok := other.(*resWrangler)
if !ok {
panic("bad cast")
return fmt.Errorf("bad cast to resWrangler 2")
}
if m.Size() != m2.Size() {
return fmt.Errorf(
@ -388,7 +394,7 @@ func (m *resWrangler) SubsetThatCouldBeReferencedByResource(
}
result := newOne()
roleBindingNamespaces := getNamespacesForRoleBinding(referrer)
for _, possibleTarget := range m.Resources() {
for _, possibleTarget := range m.rList {
id := possibleTarget.CurId()
if !id.IsNamespaceableKind() {
// A cluster-scoped resource can be referred to by anything.
@ -435,16 +441,21 @@ func getNamespacesForRoleBinding(r *resource.Resource) map[string]bool {
return result
}
func (m *resWrangler) append(res *resource.Resource) {
m.rList = append(m.rList, res)
}
// AppendAll implements ResMap.
func (m *resWrangler) AppendAll(other ResMap) error {
if other == nil {
return nil
}
for _, res := range other.Resources() {
m2, ok := other.(*resWrangler)
if !ok {
return fmt.Errorf("bad cast to resWrangler 3")
}
return m.appendAll(m2.rList)
}
// appendAll appends all the resources, error on Id collision.
func (m *resWrangler) appendAll(list []*resource.Resource) error {
for _, res := range list {
if err := m.Append(res); err != nil {
return err
}
@ -457,7 +468,11 @@ func (m *resWrangler) AbsorbAll(other ResMap) error {
if other == nil {
return nil
}
for _, r := range other.Resources() {
m2, ok := other.(*resWrangler)
if !ok {
return fmt.Errorf("bad cast to resWrangler 4")
}
for _, r := range m2.rList {
err := m.appendReplaceOrMerge(r)
if err != nil {
return err
@ -522,7 +537,7 @@ func (m *resWrangler) Select(s types.Selector) ([]*resource.Resource, error) {
if err != nil {
return nil, err
}
for _, r := range m.Resources() {
for _, r := range m.rList {
curId := r.CurId()
orgId := r.OrgId()
@ -567,77 +582,39 @@ func (m *resWrangler) Select(s types.Selector) ([]*resource.Resource, error) {
return result, nil
}
// ToRNodeSlice converts the resources in the resmp
// to a list of RNodes
func (m *resWrangler) ToRNodeSlice() ([]*kyaml_yaml.RNode, error) {
var rnodes []*kyaml_yaml.RNode
for _, r := range m.Resources() {
s, err := r.AsYAML()
if err != nil {
return nil, err
}
rnode, err := kyaml_yaml.Parse(string(s))
if err != nil {
return nil, err
}
rnodes = append(rnodes, rnode)
// ToRNodeSlice returns a copy of the resources as RNodes.
func (m *resWrangler) ToRNodeSlice() []*kyaml.RNode {
result := make([]*kyaml.RNode, len(m.rList))
for i := range m.rList {
result[i] = m.rList[i].AsRNode()
}
return rnodes, nil
return result
}
// ApplySmPatch applies the patch, and errors on Id collisions.
func (m *resWrangler) ApplySmPatch(
selectedSet *resource.IdSet, patch *resource.Resource) error {
newRm := New()
for _, res := range m.Resources() {
if !selectedSet.Contains(res.CurId()) {
newRm.Append(res)
continue
}
patchCopy := patch.DeepCopy()
patchCopy.CopyMergeMetaDataFieldsFrom(patch)
patchCopy.SetGvk(res.GetGvk())
err := res.ApplySmPatch(patchCopy)
if err != nil {
// Check for an error string from UnmarshalJSON that's indicative
// of an object that's missing basic KRM fields, and thus may have been
// entirely deleted (an acceptable outcome). This error handling should
// be deleted along with use of ResMap and apimachinery functions like
// UnmarshalJSON.
if !strings.Contains(err.Error(), "Object 'Kind' is missing") {
// Some unknown error, let it through.
var list []*resource.Resource
for _, res := range m.rList {
if selectedSet.Contains(res.CurId()) {
patchCopy := patch.DeepCopy()
patchCopy.CopyMergeMetaDataFieldsFrom(patch)
patchCopy.SetGvk(res.GetGvk())
patchCopy.SetKind(patch.GetKind())
if err := res.ApplySmPatch(patchCopy); err != nil {
return err
}
empty, err := res.IsEmpty()
if err != nil {
return err
}
if !empty {
m, _ := res.Map()
return errors.Wrapf(
err, "with unexpectedly non-empty object map of size %d",
len(m))
}
// Fall through to handle deleted object.
}
empty, err := res.IsEmpty()
if err != nil {
return err
}
if !empty {
// IsEmpty means all fields have been removed from the object.
// This can happen if a patch required deletion of the
// entire resource (not just a part of it). This means
// the overall resmap must shrink by one.
newRm.Append(res)
if !res.IsEmpty() {
list = append(list, res)
}
}
m.Clear()
m.AppendAll(newRm)
return nil
return m.appendAll(list)
}
func (m *resWrangler) RemoveBuildAnnotations() {
for _, r := range m.Resources() {
for _, r := range m.rList {
r.RemoveBuildAnnotations()
}
}

View File

@ -1,20 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package resource
import "sigs.k8s.io/kustomize/api/resid"
// ConflictDetector detects conflicts between resources.
type ConflictDetector interface {
// HasConflict returns true if the given resources have a conflict.
HasConflict(patch1, patch2 *Resource) (bool, error)
// Merge two resources into one.
MergePatches(patch1, patch2 *Resource) (*Resource, error)
}
// ConflictDetectorFactory makes instances of ConflictDetector that know
// how to handle the given Group, Version, Kind tuple.
type ConflictDetectorFactory interface {
New(gvk resid.Gvk) (ConflictDetector, error)
}

View File

@ -10,28 +10,33 @@ import (
"strings"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/generators"
"sigs.k8s.io/kustomize/api/internal/kusterr"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// Factory makes instances of Resource.
type Factory struct {
kf ifc.KunstructuredFactory
hasher ifc.KustHasher
}
// NewFactory makes an instance of Factory.
func NewFactory(kf ifc.KunstructuredFactory) *Factory {
return &Factory{kf: kf}
func NewFactory(h ifc.KustHasher) *Factory {
return &Factory{hasher: h}
}
func (rf *Factory) Hasher() ifc.KunstructuredHasher {
return rf.kf.Hasher()
// Hasher returns an ifc.KustHasher
func (rf *Factory) Hasher() ifc.KustHasher {
return rf.hasher
}
// FromMap returns a new instance of Resource.
func (rf *Factory) FromMap(m map[string]interface{}) *Resource {
return rf.makeOne(rf.kf.FromMap(m), nil)
return rf.FromMapAndOption(m, nil)
}
// FromMapWithName returns a new instance with the given "original" name.
@ -41,34 +46,30 @@ func (rf *Factory) FromMapWithName(n string, m map[string]interface{}) *Resource
// FromMapWithNamespaceAndName returns a new instance with the given "original" namespace.
func (rf *Factory) FromMapWithNamespaceAndName(ns string, n string, m map[string]interface{}) *Resource {
return rf.makeOne(rf.kf.FromMap(m), nil).setPreviousNamespaceAndName(ns, n)
r := rf.FromMapAndOption(m, nil)
return r.setPreviousId(ns, n, r.GetKind())
}
// FromMapAndOption returns a new instance of Resource with given options.
func (rf *Factory) FromMapAndOption(
m map[string]interface{}, args *types.GeneratorArgs) *Resource {
return rf.makeOne(rf.kf.FromMap(m), types.NewGenArgs(args))
}
// FromKunstructured returns a new instance of Resource.
func (rf *Factory) FromKunstructured(u ifc.Kunstructured) *Resource {
return rf.makeOne(u, nil)
n, err := yaml.FromMap(m)
if err != nil {
// TODO: return err instead of log.
log.Fatal(err)
}
return rf.makeOne(n, types.NewGenArgs(args))
}
// makeOne returns a new instance of Resource.
func (rf *Factory) makeOne(
u ifc.Kunstructured, o *types.GenArgs) *Resource {
if u == nil {
log.Fatal("unstruct ifc must not be null")
func (rf *Factory) makeOne(rn *yaml.RNode, o *types.GenArgs) *Resource {
if rn == nil {
log.Fatal("RNode must not be null")
}
if o == nil {
o = types.NewGenArgs(nil)
}
r := &Resource{
kunStr: u,
options: o,
}
return r
return &Resource{node: rn, options: o}
}
// SliceFromPatches returns a slice of resources given a patch path
@ -105,47 +106,135 @@ func (rf *Factory) FromBytes(in []byte) (*Resource, error) {
// SliceFromBytes unmarshals bytes into a Resource slice.
func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) {
kunStructs, err := rf.kf.SliceFromBytes(in)
nodes, err := rf.RNodesFromBytes(in)
if err != nil {
return nil, err
}
var result []*Resource
for len(kunStructs) > 0 {
u := kunStructs[0]
kunStructs = kunStructs[1:]
if strings.HasSuffix(u.GetKind(), "List") {
m, err := u.Map()
if err != nil {
return nil, err
return rf.resourcesFromRNodes(nodes), nil
}
// ResourcesFromRNodes converts RNodes to Resources.
func (rf *Factory) ResourcesFromRNodes(
nodes []*yaml.RNode) (result []*Resource, err error) {
nodes, err = rf.dropBadNodes(nodes)
if err != nil {
return nil, err
}
return rf.resourcesFromRNodes(nodes), nil
}
// resourcesFromRNode assumes all nodes are good.
func (rf *Factory) resourcesFromRNodes(
nodes []*yaml.RNode) (result []*Resource) {
for _, n := range nodes {
result = append(result, rf.makeOne(n, nil))
}
return
}
func (rf *Factory) RNodesFromBytes(b []byte) (result []*yaml.RNode, err error) {
nodes, err := kio.FromBytes(b)
if err != nil {
return nil, err
}
nodes, err = rf.dropBadNodes(nodes)
if err != nil {
return nil, err
}
for len(nodes) > 0 {
n0 := nodes[0]
nodes = nodes[1:]
kind := n0.GetKind()
if !strings.HasSuffix(kind, "List") {
result = append(result, n0)
continue
}
// Convert a FooList into a slice of Foo.
var m map[string]interface{}
m, err = n0.Map()
if err != nil {
return nil, err
}
items, ok := m["items"]
if !ok {
// treat as an empty list
continue
}
slice, ok := items.([]interface{})
if !ok {
if items == nil {
// an empty list
continue
}
items := m["items"]
itemsSlice, ok := items.([]interface{})
if !ok {
if items == nil {
// an empty list
continue
}
return nil, fmt.Errorf("items in List is type %T, expected array", items)
}
for _, item := range itemsSlice {
itemJSON, err := json.Marshal(item)
if err != nil {
return nil, err
}
innerU, err := rf.kf.SliceFromBytes(itemJSON)
if err != nil {
return nil, err
}
// append innerU to kunStructs so nested Lists can be handled
kunStructs = append(kunStructs, innerU...)
}
} else {
result = append(result, rf.FromKunstructured(u))
return nil, fmt.Errorf(
"expected array in %s/items, but found %T", kind, items)
}
innerNodes, err := rf.convertObjectSliceToNodeSlice(slice)
if err != nil {
return nil, err
}
nodes = append(nodes, innerNodes...)
}
return result, nil
}
// convertObjectSlice converts a list of objects to a list of RNode.
func (rf *Factory) convertObjectSliceToNodeSlice(
objects []interface{}) (result []*yaml.RNode, err error) {
var bytes []byte
var nodes []*yaml.RNode
for _, obj := range objects {
bytes, err = json.Marshal(obj)
if err != nil {
return
}
nodes, err = kio.FromBytes(bytes)
if err != nil {
return
}
nodes, err = rf.dropBadNodes(nodes)
if err != nil {
return
}
result = append(result, nodes...)
}
return
}
// dropBadNodes may drop some nodes from its input argument.
func (rf *Factory) dropBadNodes(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
var result []*yaml.RNode
for _, n := range nodes {
ignore, err := rf.shouldIgnore(n)
if err != nil {
return nil, err
}
if !ignore {
result = append(result, n)
}
}
return result, nil
}
// shouldIgnore returns true if there's some reason to ignore the node.
func (rf *Factory) shouldIgnore(n *yaml.RNode) (bool, error) {
if n.IsNilOrEmpty() {
return true, nil
}
md, err := n.GetValidatedMetadata()
if err != nil {
return true, err
}
_, ignore := md.ObjectMeta.Annotations[konfig.IgnoredByKustomizeAnnotation]
if ignore {
return true, nil
}
if foundNil, path := n.HasNilEntryInList(); foundNil {
return true, fmt.Errorf("empty item at %v in object %v", path, n)
}
return false, nil
}
// SliceFromBytesWithNames unmarshals bytes into a Resource slice with specified original
// name.
func (rf *Factory) SliceFromBytesWithNames(names []string, in []byte) ([]*Resource, error) {
@ -157,25 +246,25 @@ func (rf *Factory) SliceFromBytesWithNames(names []string, in []byte) ([]*Resour
return nil, fmt.Errorf("number of names doesn't match number of resources")
}
for i, res := range result {
res.setPreviousNamespaceAndName(resid.DefaultNamespace, names[i])
res.setPreviousId(resid.DefaultNamespace, names[i], res.GetKind())
}
return result, nil
}
// MakeConfigMap makes an instance of Resource for ConfigMap
func (rf *Factory) MakeConfigMap(kvLdr ifc.KvLoader, args *types.ConfigMapArgs) (*Resource, error) {
u, err := rf.kf.MakeConfigMap(kvLdr, args)
rn, err := generators.MakeConfigMap(kvLdr, args)
if err != nil {
return nil, err
}
return rf.makeOne(u, types.NewGenArgs(&args.GeneratorArgs)), nil
return rf.makeOne(rn, types.NewGenArgs(&args.GeneratorArgs)), nil
}
// MakeSecret makes an instance of Resource for Secret
func (rf *Factory) MakeSecret(kvLdr ifc.KvLoader, args *types.SecretArgs) (*Resource, error) {
u, err := rf.kf.MakeSecret(kvLdr, args)
rn, err := generators.MakeSecret(kvLdr, args)
if err != nil {
return nil, err
}
return rf.makeOne(u, types.NewGenArgs(&args.GeneratorArgs)), nil
return rf.makeOne(rn, types.NewGenArgs(&args.GeneratorArgs)), nil
}

View File

@ -12,153 +12,184 @@ import (
"sigs.k8s.io/kustomize/api/filters/patchstrategicmerge"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/wrappy"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/kustomize/kyaml/kio"
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/yaml"
)
// Resource is a representation of a Kubernetes Resource Model (KRM) object
// Resource is an RNode, representing a Kubernetes Resource Model object,
// paired with metadata used by kustomize.
// For more history, see sigs.k8s.io/kustomize/api/ifc.Unstructured
type Resource struct {
kunStr ifc.Kunstructured
// TODO: Inline RNode, dropping complexity. Resource is just a decorator.
node *kyaml.RNode
options *types.GenArgs
refBy []resid.ResId
refVarNames []string
}
const (
buildAnnotationPreviousKinds = konfig.ConfigAnnoDomain + "/previousKinds"
buildAnnotationPreviousNames = konfig.ConfigAnnoDomain + "/previousNames"
buildAnnotationPrefixes = konfig.ConfigAnnoDomain + "/prefixes"
buildAnnotationSuffixes = konfig.ConfigAnnoDomain + "/suffixes"
buildAnnotationPreviousNamespaces = konfig.ConfigAnnoDomain + "/previousNamespaces"
// the following are only for patches, to specify whether they can change names
// and kinds of their targets
buildAnnotationAllowNameChange = konfig.ConfigAnnoDomain + "/allowNameChange"
buildAnnotationAllowKindChange = konfig.ConfigAnnoDomain + "/allowKindChange"
)
var buildAnnotations = []string{
buildAnnotationPreviousKinds,
buildAnnotationPreviousNames,
buildAnnotationPrefixes,
buildAnnotationSuffixes,
buildAnnotationPreviousNamespaces,
buildAnnotationAllowNameChange,
buildAnnotationAllowKindChange,
}
func (r *Resource) AsRNode() *kyaml.RNode {
return r.node.Copy()
}
func (r *Resource) ResetPrimaryData(incoming *Resource) {
r.kunStr = incoming.Copy()
r.node = incoming.node.Copy()
}
func (r *Resource) GetAnnotations() map[string]string {
annotations := r.kunStr.GetAnnotations()
if annotations == nil {
annotations, err := r.node.GetAnnotations()
if err != nil || annotations == nil {
return make(map[string]string)
}
return annotations
}
func (r *Resource) Copy() ifc.Kunstructured {
return r.kunStr.Copy()
}
func (r *Resource) GetFieldValue(f string) (interface{}, error) {
return r.kunStr.GetFieldValue(f)
//nolint:staticcheck
return r.node.GetFieldValue(f)
}
func (r *Resource) GetDataMap() map[string]string {
return r.kunStr.GetDataMap()
return r.node.GetDataMap()
}
func (r *Resource) GetBinaryDataMap() map[string]string {
return r.kunStr.GetBinaryDataMap()
return r.node.GetBinaryDataMap()
}
func (r *Resource) GetGvk() resid.Gvk {
return r.kunStr.GetGvk()
meta, err := r.node.GetMeta()
if err != nil {
return resid.GvkFromString("")
}
g, v := resid.ParseGroupVersion(meta.APIVersion)
return resid.Gvk{Group: g, Version: v, Kind: meta.Kind}
}
func (r *Resource) Hash(h ifc.KustHasher) (string, error) {
return h.Hash(r.node)
}
func (r *Resource) GetKind() string {
return r.kunStr.GetKind()
return r.node.GetKind()
}
func (r *Resource) GetLabels() map[string]string {
return r.kunStr.GetLabels()
l, err := r.node.GetLabels()
if err != nil {
return map[string]string{}
}
return l
}
func (r *Resource) GetName() string {
return r.kunStr.GetName()
return r.node.GetName()
}
func (r *Resource) GetSlice(p string) ([]interface{}, error) {
return r.kunStr.GetSlice(p)
//nolint:staticcheck
return r.node.GetSlice(p)
}
func (r *Resource) GetString(p string) (string, error) {
return r.kunStr.GetString(p)
//nolint:staticcheck
return r.node.GetString(p)
}
func (r *Resource) IsEmpty() (bool, error) {
m, err := r.kunStr.Map()
return len(m) == 0, err
func (r *Resource) IsEmpty() bool {
return r.node.IsNilOrEmpty()
}
func (r *Resource) Map() (map[string]interface{}, error) {
return r.kunStr.Map()
return r.node.Map()
}
func (r *Resource) MarshalJSON() ([]byte, error) {
return r.kunStr.MarshalJSON()
return r.node.MarshalJSON()
}
func (r *Resource) MatchesLabelSelector(selector string) (bool, error) {
return r.kunStr.MatchesLabelSelector(selector)
return r.node.MatchesLabelSelector(selector)
}
func (r *Resource) MatchesAnnotationSelector(selector string) (bool, error) {
return r.kunStr.MatchesAnnotationSelector(selector)
return r.node.MatchesAnnotationSelector(selector)
}
func (r *Resource) SetAnnotations(m map[string]string) {
if len(m) == 0 {
// Force field erasure.
r.kunStr.SetAnnotations(nil)
r.node.SetAnnotations(nil)
return
}
r.kunStr.SetAnnotations(m)
r.node.SetAnnotations(m)
}
func (r *Resource) SetDataMap(m map[string]string) {
r.kunStr.SetDataMap(m)
r.node.SetDataMap(m)
}
func (r *Resource) SetBinaryDataMap(m map[string]string) {
r.kunStr.SetBinaryDataMap(m)
r.node.SetBinaryDataMap(m)
}
func (r *Resource) SetGvk(gvk resid.Gvk) {
r.kunStr.SetGvk(gvk)
r.node.SetMapField(
kyaml.NewScalarRNode(gvk.Kind), kyaml.KindField)
r.node.SetMapField(
kyaml.NewScalarRNode(gvk.ApiVersion()), kyaml.APIVersionField)
}
func (r *Resource) SetLabels(m map[string]string) {
if len(m) == 0 {
// Force field erasure.
r.kunStr.SetLabels(nil)
r.node.SetLabels(nil)
return
}
r.kunStr.SetLabels(m)
r.node.SetLabels(m)
}
func (r *Resource) SetName(n string) {
r.kunStr.SetName(n)
r.node.SetName(n)
}
func (r *Resource) SetNamespace(n string) {
r.kunStr.SetNamespace(n)
r.node.SetNamespace(n)
}
func (r *Resource) SetKind(k string) {
gvk := r.GetGvk()
gvk.Kind = k
r.SetGvk(gvk)
}
func (r *Resource) UnmarshalJSON(s []byte) error {
return r.kunStr.UnmarshalJSON(s)
return r.node.UnmarshalJSON(s)
}
// ResCtx is an interface describing the contextual added
@ -178,14 +209,14 @@ type ResCtxMatcher func(ResCtx) bool
// DeepCopy returns a new copy of resource
func (r *Resource) DeepCopy() *Resource {
rc := &Resource{
kunStr: r.Copy(),
node: r.node.Copy(),
}
rc.copyOtherFields(r)
return rc
}
// CopyMergeMetaDataFields copies everything but the non-metadata in
// the ifc.Kunstructured map, merging labels and annotations.
// the resource.
func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) {
r.SetLabels(mergeStringMaps(other.GetLabels(), r.GetLabels()))
r.SetAnnotations(
@ -251,8 +282,10 @@ func (r *Resource) ReferencesEqual(other *Resource) bool {
return len(setSelf) == len(setOther)
}
func (r *Resource) KunstructEqual(o *Resource) bool {
return reflect.DeepEqual(r.kunStr, o.kunStr)
// NodeEqual returns true if the resource's nodes are
// equal, ignoring ancillary information like genargs, refby, etc.
func (r *Resource) NodeEqual(o *Resource) bool {
return reflect.DeepEqual(r.node, o.node)
}
func (r *Resource) copyRefBy() []resid.ResId {
@ -351,12 +384,41 @@ func (r *Resource) RemoveBuildAnnotations() {
r.SetAnnotations(annotations)
}
func (r *Resource) setPreviousNamespaceAndName(ns string, n string) *Resource {
func (r *Resource) setPreviousId(ns string, n string, k string) *Resource {
r.appendCsvAnnotation(buildAnnotationPreviousNames, n)
r.appendCsvAnnotation(buildAnnotationPreviousNamespaces, ns)
r.appendCsvAnnotation(buildAnnotationPreviousKinds, k)
return r
}
func (r *Resource) SetAllowNameChange(value string) {
annotations := r.GetAnnotations()
annotations[buildAnnotationAllowNameChange] = value
r.SetAnnotations(annotations)
}
func (r *Resource) NameChangeAllowed() bool {
annotations := r.GetAnnotations()
if allowed, set := annotations[buildAnnotationAllowNameChange]; set && allowed == "true" {
return true
}
return false
}
func (r *Resource) SetAllowKindChange(value string) {
annotations := r.GetAnnotations()
annotations[buildAnnotationAllowKindChange] = value
r.SetAnnotations(annotations)
}
func (r *Resource) KindChangeAllowed() bool {
annotations := r.GetAnnotations()
if allowed, set := annotations[buildAnnotationAllowKindChange]; set && allowed == "true" {
return true
}
return false
}
// String returns resource as JSON.
func (r *Resource) String() string {
bs, err := r.MarshalJSON()
@ -430,14 +492,19 @@ func (r *Resource) PrevIds() []resid.ResId {
// pairs on one annotation so there is no chance of error
names := r.getCsvAnnotation(buildAnnotationPreviousNames)
ns := r.getCsvAnnotation(buildAnnotationPreviousNamespaces)
if len(names) != len(ns) {
kinds := r.getCsvAnnotation(buildAnnotationPreviousKinds)
if len(names) != len(ns) || len(names) != len(kinds) {
panic(errors.New(
"number of previous names not equal to " +
"number of previous namespaces"))
"number of previous names, " +
"number of previous namespaces, " +
"number of previous kinds not equal"))
}
for i := range names {
k := kinds[i]
gvk := r.GetGvk()
gvk.Kind = k
ids = append(ids, resid.NewResIdWithNamespace(
r.GetGvk(), names[i], ns[i]))
gvk, names[i], ns[i]))
}
return ids
}
@ -445,7 +512,7 @@ func (r *Resource) PrevIds() []resid.ResId {
// StorePreviousId stores the resource's current ID via build annotations.
func (r *Resource) StorePreviousId() {
id := r.CurId()
r.setPreviousNamespaceAndName(id.EffectiveNamespace(), id.Name)
r.setPreviousId(id.EffectiveNamespace(), id.Name, id.Kind)
}
// CurId returns a ResId for the resource using the
@ -478,38 +545,35 @@ func (r *Resource) AppendRefVarName(variable types.Var) {
// ApplySmPatch applies the provided strategic merge patch.
func (r *Resource) ApplySmPatch(patch *Resource) error {
node, err := filtersutil.GetRNode(patch)
if err != nil {
n, ns, k := r.GetName(), r.GetNamespace(), r.GetKind()
if patch.NameChangeAllowed() || patch.KindChangeAllowed() {
r.StorePreviousId()
}
if err := r.ApplyFilter(patchstrategicmerge.Filter{
Patch: patch.node,
}); err != nil {
return err
}
n, ns := r.GetName(), r.GetNamespace()
err = r.ApplyFilter(patchstrategicmerge.Filter{
Patch: node,
})
if err != nil {
return err
if r.IsEmpty() {
return nil
}
empty, err := r.IsEmpty()
if err != nil {
return err
if !patch.KindChangeAllowed() {
r.SetKind(k)
}
if !empty {
if !patch.NameChangeAllowed() {
r.SetName(n)
r.SetNamespace(ns)
}
return err
r.SetNamespace(ns)
return nil
}
func (r *Resource) ApplyFilter(f kio.Filter) error {
if wn, ok := r.kunStr.(*wrappy.WNode); ok {
l, err := f.Filter([]*kyaml.RNode{wn.AsRNode()})
if len(l) == 0 {
// Hack to deal with deletion.
r.kunStr = wrappy.NewWNode()
}
return err
l, err := f.Filter([]*kyaml.RNode{r.node})
if len(l) == 0 {
// The node was deleted. The following makes r.IsEmpty() true.
r.node = nil
}
return filtersutil.ApplyToJSON(f, r)
return err
}
func mergeStringMaps(maps ...map[string]string) map[string]string {

View File

@ -23,7 +23,7 @@ func (e *errUnableToFind) Error() string {
m = append(m, "('"+p.Value+"'; "+p.Key+")")
}
return fmt.Sprintf(
"unable to find plugin root - tried: %s", strings.Join(m, ", "))
"unable to find %s - tried: %s", e.what, strings.Join(m, ", "))
}
func NewErrUnableToFind(w string, a []Pair) *errUnableToFind {

View File

@ -9,8 +9,7 @@ import (
"sigs.k8s.io/kustomize/api/resid"
)
// FieldSpec completely specifies a kustomizable field in
// an unstructured representation of a k8s API object.
// FieldSpec completely specifies a kustomizable field in a k8s API object.
// It helps define the operands of transformations.
//
// For example, a directive to add a common label to objects

View File

@ -3,14 +3,77 @@
package types
// HelmChartArgs contains the metadata of how to generate a secret.
type HelmGlobals struct {
// ChartHome is a file path, relative to the kustomization root,
// to a directory containing a subdirectory for each chart to be
// included in the kustomization.
// The default value of this field is "charts".
// So, for example, kustomize looks for the minecraft chart
// at {kustomizationRoot}/{ChartHome}/minecraft.
// If the chart is there at build time, kustomize will use it as found,
// and not check version numbers or dates.
// If the chart is not there, kustomize will attempt to pull it
// using the version number specified in the kustomization file,
// and put it there. To suppress the pull attempt, simply assure
// that the chart is already there.
ChartHome string `json:"chartHome,omitempty" yaml:"chartHome,omitempty"`
// ConfigHome defines a value that kustomize should pass to helm via
// the HELM_CONFIG_HOME environment variable. kustomize doesn't attempt
// to read or write this directory.
// If omitted, {tmpDir}/helm is used, where {tmpDir} is some temporary
// directory created by kustomize for the benefit of helm.
// Likewise, kustomize sets
// HELM_CACHE_HOME={ConfigHome}/.cache
// HELM_DATA_HOME={ConfigHome}/.data
// for the helm subprocess.
ConfigHome string `json:"configHome,omitempty" yaml:"configHome,omitempty"`
}
type HelmChart struct {
// Name is the name of the chart, e.g. 'minecraft'.
Name string `json:"name,omitempty" yaml:"name,omitempty"`
// Version is the version of the chart, e.g. '3.1.3'
Version string `json:"version,omitempty" yaml:"version,omitempty"`
// Repo is a URL locating the chart on the internet.
// This is the argument to helm's `--repo` flag, e.g.
// `https://itzg.github.io/minecraft-server-charts`.
Repo string `json:"repo,omitempty" yaml:"repo,omitempty"`
// ReleaseName replaces RELEASE-NAME in chart template output,
// making a particular inflation of a chart unique with respect to
// other inflations of the same chart in a cluster. It's the first
// argument to the helm `install` and `template` commands, i.e.
// helm install {RELEASE-NAME} {chartName}
// helm template {RELEASE-NAME} {chartName}
// If omitted, the flag --generate-name is passed to 'helm template'.
ReleaseName string `json:"releaseName,omitempty" yaml:"releaseName,omitempty"`
// ValuesFile is local file path to a values file to use _instead of_
// the default values that accompanied the chart.
// The default values are in '{ChartHome}/{Name}/values.yaml'.
ValuesFile string `json:"valuesFile,omitempty" yaml:"valuesFile,omitempty"`
// ValuesInline holds value mappings specified directly,
// rather than in a separate file.
ValuesInline map[string]interface{} `json:"valuesInline,omitempty" yaml:"valuesInline,omitempty"`
// ValuesMerge specifies how to treat ValuesInline with respect to Values.
// Legal values: 'merge', 'override', 'replace'.
// Defaults to 'override'.
ValuesMerge string `json:"valuesMerge,omitempty" yaml:"valuesMerge,omitempty"`
}
// HelmChartArgs contains arguments to helm.
// Deprecated. Use HelmGlobals and HelmChart instead.
type HelmChartArgs struct {
ChartName string `json:"chartName,omitempty" yaml:"chartName,omitempty"`
ChartVersion string `json:"chartVersion,omitempty" yaml:"chartVersion,omitempty"`
ChartRepoURL string `json:"chartRepoUrl,omitempty" yaml:"chartRepoUrl,omitempty"`
ChartHome string `json:"chartHome,omitempty" yaml:"chartHome,omitempty"`
// Use chartRelease to keep compatible with old exec plugin
ChartRepoName string `json:"chartRelease,omitempty" yaml:"chartRelease,omitempty"`
ChartName string `json:"chartName,omitempty" yaml:"chartName,omitempty"`
ChartVersion string `json:"chartVersion,omitempty" yaml:"chartVersion,omitempty"`
ChartRepoURL string `json:"chartRepoUrl,omitempty" yaml:"chartRepoUrl,omitempty"`
ChartHome string `json:"chartHome,omitempty" yaml:"chartHome,omitempty"`
ChartRepoName string `json:"chartRepoName,omitempty" yaml:"chartRepoName,omitempty"`
HelmBin string `json:"helmBin,omitempty" yaml:"helmBin,omitempty"`
HelmHome string `json:"helmHome,omitempty" yaml:"helmHome,omitempty"`
Values string `json:"values,omitempty" yaml:"values,omitempty"`
@ -20,3 +83,32 @@ type HelmChartArgs struct {
ReleaseNamespace string `json:"releaseNamespace,omitempty" yaml:"releaseNamespace,omitempty"`
ExtraArgs []string `json:"extraArgs,omitempty" yaml:"extraArgs,omitempty"`
}
// SplitHelmParameters splits helm parameters into
// per-chart params and global chart-independent parameters.
func SplitHelmParameters(
oldArgs []HelmChartArgs) (charts []HelmChart, globals HelmGlobals) {
for _, old := range oldArgs {
charts = append(charts, makeHelmChartFromHca(&old))
if old.HelmHome != "" {
// last non-empty wins
globals.ConfigHome = old.HelmHome
}
if old.ChartHome != "" {
// last non-empty wins
globals.ChartHome = old.ChartHome
}
}
return charts, globals
}
func makeHelmChartFromHca(old *HelmChartArgs) (c HelmChart) {
c.Name = old.ChartName
c.Version = old.ChartVersion
c.Repo = old.ChartRepoURL
c.ValuesFile = old.Values
c.ValuesInline = old.ValuesLocal
c.ValuesMerge = old.ValuesMerge
c.ReleaseName = old.ReleaseName
return
}

View File

@ -6,6 +6,7 @@ package types
import (
"bytes"
"encoding/json"
"fmt"
"sigs.k8s.io/yaml"
)
@ -46,6 +47,9 @@ type Kustomization struct {
// CommonLabels to add to all objects and selectors.
CommonLabels map[string]string `json:"commonLabels,omitempty" yaml:"commonLabels,omitempty"`
// Labels to add to all objects but not selectors.
Labels []Label `json:"labels,omitempty" yaml:"labels,omitempty"`
// CommonAnnotations to add to all objects.
CommonAnnotations map[string]string `json:"commonAnnotations,omitempty" yaml:"commonAnnotations,omitempty"`
@ -125,9 +129,14 @@ type Kustomization struct {
// the map will have a suffix hash generated from its contents.
SecretGenerator []SecretArgs `json:"secretGenerator,omitempty" yaml:"secretGenerator,omitempty"`
// HelmGlobals contains helm configuration that isn't chart specific.
HelmGlobals *HelmGlobals `json:"helmGlobals,omitempty" yaml:"helmGlobals,omitempty"`
// HelmCharts is a list of helm chart configuration instances.
HelmCharts []HelmChart `json:"helmCharts,omitempty" yaml:"helmCharts,omitempty"`
// HelmChartInflationGenerator is a list of helm chart configurations.
// The resulting resource is a normal operand rendered from
// a remote chart by `helm template`
// Deprecated. Auto-converted to HelmGlobals and HelmCharts.
HelmChartInflationGenerator []HelmChartArgs `json:"helmChartInflationGenerator,omitempty" yaml:"helmChartInflationGenerator,omitempty"`
// GeneratorOptions modify behavior of all ConfigMap and Secret generators.
@ -181,15 +190,42 @@ func (k *Kustomization) FixKustomizationPostUnmarshalling() {
k.SecretGenerator[i].EnvSource = ""
}
}
charts, globals := SplitHelmParameters(k.HelmChartInflationGenerator)
if k.HelmGlobals == nil {
if globals.ChartHome != "" || globals.ConfigHome != "" {
k.HelmGlobals = &globals
}
}
k.HelmCharts = append(k.HelmCharts, charts...)
// Wipe it for the fix command.
k.HelmChartInflationGenerator = nil
}
// FixKustomizationPreMarshalling fixes things
// that should occur after the kustomization file
// has been processed.
func (k *Kustomization) FixKustomizationPreMarshalling() {
func (k *Kustomization) FixKustomizationPreMarshalling() error {
// PatchesJson6902 should be under the Patches field.
k.Patches = append(k.Patches, k.PatchesJson6902...)
k.PatchesJson6902 = nil
// this fix is not in FixKustomizationPostUnmarshalling because
// it will break some commands like `create` and `add`. those
// commands depend on 'commonLabels' field
if cl := labelFromCommonLabels(k.CommonLabels); cl != nil {
// check conflicts between commonLabels and labels
for _, l := range k.Labels {
for k := range l.Pairs {
if _, exist := cl.Pairs[k]; exist {
return fmt.Errorf("label name '%s' exists in both commonLabels and labels", k)
}
}
}
k.Labels = append(k.Labels, *cl)
k.CommonLabels = nil
}
return nil
}
func (k *Kustomization) EnforceFields() []string {

25
vendor/sigs.k8s.io/kustomize/api/types/labels.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
type Label struct {
// Pairs contains the key-value pairs for labels to add
Pairs map[string]string `json:"pairs,omitempty" yaml:"pairs,omitempty"`
// IncludeSelectors inidicates should transformer include the
// fieldSpecs for selectors. Custom fieldSpecs specified by
// FieldSpecs will be merged with builtin fieldSpecs if this
// is true.
IncludeSelectors bool `json:"includeSelectors,omitempty" yaml:"includeSelectors,omitempty"`
FieldSpecs []FieldSpec `json:"fields,omitempty" yaml:"fields,omitempty"`
}
func labelFromCommonLabels(commonLabels map[string]string) *Label {
if len(commonLabels) == 0 {
return nil
}
return &Label{
Pairs: commonLabels,
IncludeSelectors: true,
}
}

View File

@ -3,6 +3,8 @@
package types
import "reflect"
// Patch represent either a Strategic Merge Patch or a JSON patch
// and its targets.
// The content of the patch can either be from a file
@ -16,6 +18,9 @@ type Patch struct {
// Target points to the resources that the patch is applied to
Target *Selector `json:"target,omitempty" yaml:"target,omitempty"`
// Options is a list of options for the patch
Options map[string]bool `json:"options,omitempty" yaml:"options,omitempty"`
}
// Equals return true if p equals o.
@ -24,5 +29,6 @@ func (p *Patch) Equals(o Patch) bool {
(p.Target != nil && o.Target != nil && *p.Target == *o.Target)
return p.Path == o.Path &&
p.Patch == o.Patch &&
targetEqual
targetEqual &&
reflect.DeepEqual(p.Options, o.Options)
}

View File

@ -3,27 +3,13 @@
package types
type HelmConfig struct {
Enabled bool
Command string
}
// PluginConfig holds plugin configuration.
type PluginConfig struct {
// AbsPluginHome is the home of kustomize plugins.
// Kustomize plugin configuration files are k8s-style objects
// containing the fields 'apiVersion' and 'kind', e.g.
// apiVersion: apps/v1
// kind: Deployment
// kustomize reads plugin configuration data from a file path
// specified in the 'generators:' or 'transformers:' field of a
// kustomization file. kustomize must then use this data to both
// locate the plugin and configure it.
// Every kustomize plugin (its code, its tests, its supporting data
// files, etc.) must be housed in its own directory at
// ${AbsPluginHome}/${pluginApiVersion}/LOWERCASE(${pluginKind})
// where
// - ${AbsPluginHome} is an absolute path, defined below.
// - ${pluginApiVersion} is taken from the plugin config file.
// - ${pluginKind} is taken from the plugin config file.
// The value of AbsPluginHome can be any absolute path.
AbsPluginHome string
// PluginRestrictions distinguishes plugin restrictions.
PluginRestrictions PluginRestrictions
@ -32,4 +18,30 @@ type PluginConfig struct {
// FnpLoadingOptions sets the way function-based plugin behaviors.
FnpLoadingOptions FnPluginLoadingOptions
// HelmConfig contains metadata needed for allowing and running helm.
HelmConfig HelmConfig
}
func EnabledPluginConfig(b BuiltinPluginLoadingOptions) (pc *PluginConfig) {
pc = MakePluginConfig(PluginRestrictionsNone, b)
pc.FnpLoadingOptions.EnableStar = true
pc.HelmConfig.Enabled = true
// If this command is not on PATH, tests needing it should skip.
pc.HelmConfig.Command = "helmV3"
return
}
func DisabledPluginConfig() *PluginConfig {
return MakePluginConfig(
PluginRestrictionsBuiltinsOnly,
BploUseStaticallyLinked)
}
func MakePluginConfig(pr PluginRestrictions,
b BuiltinPluginLoadingOptions) *PluginConfig {
return &PluginConfig{
PluginRestrictions: pr,
BpLoadingOptions: b,
}
}

View File

@ -1,27 +1,59 @@
// Copyright 2019 The Kubernetes Authors.
// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
const DefaultReplacementFieldPath = "metadata.name"
// Replacement defines how to perform a substitution
// where it is from and where it is to.
type Replacement struct {
Source *ReplSource `json:"source" yaml:"source"`
Target *ReplTarget `json:"target" yaml:"target"`
// The source of the value.
Source *SourceSelector `json:"source" yaml:"source"`
// The N fields to write the value to.
Targets []*TargetSelector `json:"targets" yaml:"targets"`
}
// ReplSource defines where a substitution is from
// It can from two different kinds of sources
// - from a field of one resource
// - from a string
type ReplSource struct {
ObjRef *Target `json:"objref,omitempty" yaml:"objref,omitempty"`
FieldRef string `json:"fieldref,omitempty" yaml:"fiedldref,omitempty"`
Value string `json:"value,omitempty" yaml:"value,omitempty"`
// SourceSelector is the source of the replacement transformer.
type SourceSelector struct {
// A specific object to read it from.
KrmId `json:",inline,omitempty" yaml:",inline,omitempty"`
// Structured field path expected in the allowed object.
FieldPath string `json:"fieldPath" yaml:"fieldPath"`
// Used to refine the interpretation of the field.
Options *FieldOptions `json:"options" yaml:"options"`
}
// ReplTarget defines where a substitution is to.
type ReplTarget struct {
ObjRef *Selector `json:"objref,omitempty" yaml:"objref,omitempty"`
FieldRefs []string `json:"fieldrefs,omitempty" yaml:"fieldrefs,omitempty"`
// TargetSelector specifies fields in one or more objects.
type TargetSelector struct {
// Include objects that match this.
Select *Selector `json:"select" yaml:"select"`
// From the allowed set, remove objects that match this.
Reject []*Selector `json:"reject" yaml:"reject"`
// Structured field paths expected in each allowed object.
FieldPaths []string `json:"fieldPaths" yaml:"fieldPaths"`
// Used to refine the interpretation of the field.
Options *FieldOptions `json:"options" yaml:"options"`
}
// FieldOptions refine the interpretation of FieldPaths.
type FieldOptions struct {
// Used to split/join the field.
Delimiter string `json:"delimiter" yaml:"delimiter"`
// Which position in the split to consider.
Index int `json:"index" yaml:"index"`
// TODO (#3492): Implement use of this option
// None, Base64, URL, Hex, etc
Encoding string `json:"encoding" yaml:"encoding"`
// If field missing, add it.
Create bool `json:"create" yaml:"create"`
}

View File

@ -13,9 +13,8 @@ import (
// Any resource that matches intersection of all conditions
// is included in this set.
type Selector struct {
resid.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
// KrmId refers to a GVKN/Ns of a resource.
KrmId `json:",inline,omitempty" yaml:",inline,omitempty"`
// AnnotationSelector is a string that follows the label selection expression
// https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api
@ -28,6 +27,23 @@ type Selector struct {
LabelSelector string `json:"labelSelector,omitempty" yaml:"labelSelector,omitempty"`
}
// KrmId refers to a GVKN/Ns of a resource.
type KrmId struct {
resid.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
}
// Match returns true if id selects other, i.e. id's fields
// either match other's or are empty
func (id *KrmId) Match(other *KrmId) bool {
return (id.Group == "" || id.Group == other.Group) &&
(id.Version == "" || id.Version == other.Version) &&
(id.Kind == "" || id.Kind == other.Kind) &&
(id.Name == "" || id.Name == other.Name) &&
(id.Namespace == "" || id.Namespace == other.Namespace)
}
// SelectorRegex is a Selector with regex in GVK
// Any resource that matches intersection of all conditions
// is included in this set.

View File

@ -12,8 +12,6 @@ import (
"sigs.k8s.io/kustomize/api/resid"
)
const defaultFieldPath = "metadata.name"
// Var represents a variable whose value will be sourced
// from a field in a Kubernetes object.
type Var struct {
@ -71,7 +69,7 @@ type FieldSelector struct {
// defaulting sets reference to field used by default.
func (v *Var) Defaulting() {
if v.FieldRef.FieldPath == "" {
v.FieldRef.FieldPath = defaultFieldPath
v.FieldRef.FieldPath = DefaultReplacementFieldPath
}
v.ObjRef.GVK()
}

View File

@ -6,7 +6,6 @@ package build
import (
"fmt"
"io"
"log"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/api/filesys"
@ -22,10 +21,11 @@ var theArgs struct {
var theFlags struct {
outputPath string
enable struct {
resourceIdChanges bool
plugins bool
managedByLabel bool
plugins bool
managedByLabel bool
helm bool
}
helmCommand string
loadRestrictor string
reorderOutput string
fnOptions types.FnPluginLoadingOptions
@ -104,7 +104,7 @@ func NewCmdBuild(
AddFlagEnablePlugins(cmd.Flags())
AddFlagReorderOutput(cmd.Flags())
AddFlagEnableManagedbyLabel(cmd.Flags())
AddFlagAllowResourceIdChanges(cmd.Flags())
AddFlagEnableHelm(cmd.Flags())
return cmd
}
@ -132,14 +132,13 @@ func HonorKustomizeFlags(kOpts *krusty.Options) *krusty.Options {
kOpts.DoLegacyResourceSort = getFlagReorderOutput() == legacy
kOpts.LoadRestrictions = getFlagLoadRestrictorValue()
if theFlags.enable.plugins {
c, err := konfig.EnabledPluginConfig(types.BploUseStaticallyLinked)
if err != nil {
log.Fatal(err)
}
c := types.EnabledPluginConfig(types.BploUseStaticallyLinked)
c.FnpLoadingOptions = theFlags.fnOptions
kOpts.PluginConfig = c
} else {
kOpts.PluginConfig.HelmConfig.Enabled = theFlags.enable.helm
}
kOpts.PluginConfig.HelmConfig.Command = theFlags.helmCommand
kOpts.AddManagedbyLabel = isManagedByLabelEnabled()
kOpts.AllowResourceIdChanges = theFlags.enable.resourceIdChanges
return kOpts
}

View File

@ -1,16 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package build
import (
"github.com/spf13/pflag"
)
func AddFlagAllowResourceIdChanges(set *pflag.FlagSet) {
set.BoolVar(
&theFlags.enable.resourceIdChanges,
"allow-id-changes",
false,
`enable changes to a resourceId`)
}

View File

@ -0,0 +1,24 @@
// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package build
import (
"github.com/spf13/pflag"
)
// AddFlagEnableHelm adds the --enable-helm flag.
// The helm plugin is builtin, meaning it's
// enabled independently of --enable-alpha-plugins.
func AddFlagEnableHelm(set *pflag.FlagSet) {
set.BoolVar(
&theFlags.enable.helm,
"enable-helm",
false,
"Enable use of the Helm chart inflator generator.")
set.StringVar(
&theFlags.helmCommand,
"helm-command",
"helm", // default
"helm command (path to executable)")
}

View File

@ -1,6 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package filtersutil provides utilities for working with yaml.Filter and
// kio.Filter interfaces.
package filtersutil

View File

@ -1,83 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filtersutil
import (
"encoding/json"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// ApplyToJSON applies the filter to the json objects.
//
// ApplyToJSON marshals the objects into a slice of yaml.RNodes, runs
// the filter on the slice, and then unmarshals the values back.
//
// The filter must not create or delete objects because the objects
// are updated in place.
func ApplyToJSON(filter kio.Filter, objs ...marshalerUnmarshaler) error {
var nodes []*yaml.RNode
// convert the json objects to rnodes
for i := range objs {
node, err := GetRNode(objs[i])
if err != nil {
return err
}
nodes = append(nodes, node)
}
// apply the filter
nodes, err := filter.Filter(nodes)
if err != nil {
return err
}
if len(nodes) != len(objs) {
return errors.Errorf("filter cannot create or delete objects")
}
// convert the rnodes to json objects
for i := range nodes {
err = setRNode(objs[i], nodes[i])
if err != nil {
return err
}
}
return nil
}
type marshalerUnmarshaler interface {
json.Unmarshaler
json.Marshaler
}
// GetRNode converts k into an RNode
func GetRNode(k json.Marshaler) (*yaml.RNode, error) {
j, err := k.MarshalJSON()
if err != nil {
return nil, err
}
return yaml.Parse(string(j))
}
// setRNode marshals node into k
func setRNode(k json.Unmarshaler, node *yaml.RNode) error {
s, err := node.String()
if err != nil {
return err
}
m := map[string]interface{}{}
if err := yaml.Unmarshal([]byte(s), &m); err != nil {
return err
}
b, err := json.Marshal(m)
if err != nil {
return err
}
return k.UnmarshalJSON(b)
}

View File

@ -71,6 +71,10 @@ type LocalPackageReadWriter struct {
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) {
@ -81,6 +85,7 @@ func (r *LocalPackageReadWriter) Read() ([]*yaml.RNode, error) {
ErrorIfNonResources: r.ErrorIfNonResources,
SetAnnotations: r.SetAnnotations,
PackageFileName: r.PackageFileName,
FileSkipFunc: r.FileSkipFunc,
}.Read()
if err != nil {
return nil, errors.Wrap(err)
@ -133,6 +138,11 @@ func (r *LocalPackageReadWriter) getFiles(nodes []*yaml.RNode) (sets.String, err
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"`
@ -163,6 +173,10 @@ type LocalPackageReader struct {
// 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{}
@ -215,24 +229,24 @@ func (r LocalPackageReader) Read() ([]*yaml.RNode, error) {
if info.IsDir() {
return r.shouldSkipDir(path, ignoreFilesMatcher)
}
if match, err := r.shouldSkipFile(path, ignoreFilesMatcher); err != nil {
// 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 {
} else if match {
// skip this file
return nil
}
// get the relative path to file within the package so we can write the files back out
// to another location.
path, err = filepath.Rel(pathRelativeTo, path)
r.initReaderAnnotations(relPath, info)
nodes, err := r.readFile(path, info)
if err != nil {
return errors.WrapPrefixf(err, pathRelativeTo)
}
r.initReaderAnnotations(path, info)
nodes, err := r.readFile(filepath.Join(pathRelativeTo, path), info)
if err != nil {
return errors.WrapPrefixf(err, filepath.Join(pathRelativeTo, path))
return errors.WrapPrefixf(err, path)
}
operand = append(operand, nodes...)
return nil
@ -258,21 +272,25 @@ func (r *LocalPackageReader) readFile(path string, _ os.FileInfo) ([]*yaml.RNode
}
// shouldSkipFile returns true if the file should be skipped
func (r *LocalPackageReader) shouldSkipFile(path string, matcher *ignoreFilesMatcher) (bool, error) {
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 false, nil
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 false, errors.Wrap(err)
return true, errors.Wrap(err)
} else if match {
return true, nil
return false, nil
}
}
return false, nil
return true, nil
}
// initReaderAnnotations adds the LocalPackageReader Annotations to r.SetAnnotations

View File

@ -1,7 +1,10 @@
# Copyright 2020 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
MYGOBIN := $(shell go env GOPATH)/bin
MYGOBIN = $(shell go env GOBIN)
ifeq ($(MYGOBIN),)
MYGOBIN = $(shell go env GOPATH)/bin
endif
API_VERSION := "v1.19.1"
.PHONY: all

View File

@ -44,10 +44,9 @@ type openapiData struct {
// Kubernetes schema as part of the global schema
noUseBuiltInSchema bool
// currentOpenAPIVersion stores the version if the kubernetes openapi data
// that is currently stored as the schema, so that we only reparse the
// schema when necessary (to speed up performance)
currentOpenAPIVersion string
// schemaInit stores whether or not we've parsed the schema already,
// so that we only reparse the when necessary (to speed up performance)
schemaInit bool
}
// ResourceSchema wraps the OpenAPI Schema.
@ -477,27 +476,28 @@ func GetSchemaVersion() string {
// initSchema parses the json schema
func initSchema() {
if globalSchema.schemaInit {
return
}
globalSchema.schemaInit = true
if customSchema != nil {
ResetOpenAPI()
err := parse(customSchema)
if err != nil {
panic("invalid schema file")
}
if err := parse(kustomizationapi.MustAsset(kustomizationAPIAssetName)); err != nil {
if err = parse(kustomizationapi.MustAsset(kustomizationAPIAssetName)); err != nil {
// this should never happen
panic(err)
}
return
}
currentVersion := kubernetesOpenAPIVersion
if currentVersion == "" {
currentVersion = kubernetesOpenAPIDefaultVersion
if kubernetesOpenAPIVersion == "" {
parseBuiltinSchema(kubernetesOpenAPIDefaultVersion)
} else {
parseBuiltinSchema(kubernetesOpenAPIVersion)
}
if globalSchema.currentOpenAPIVersion != currentVersion {
parseBuiltinSchema(currentVersion)
}
globalSchema.currentOpenAPIVersion = currentVersion
}
// parseBuiltinSchema calls parse to parse the json schemas

View File

@ -8,6 +8,7 @@ import (
"fmt"
"io/ioutil"
"log"
"regexp"
"strconv"
"strings"
@ -336,6 +337,33 @@ func (rn *RNode) SetYNode(node *yaml.Node) {
*rn.value = *node
}
// GetKind returns the kind.
func (rn *RNode) GetKind() string {
node, err := rn.Pipe(FieldMatcher{Name: KindField})
if err != nil {
return ""
}
return GetValue(node)
}
// GetName returns the name.
func (rn *RNode) GetName() string {
f := rn.Field(MetadataField)
if f.IsNilOrEmpty() {
return ""
}
f = f.Value.Field(NameField)
if f.IsNilOrEmpty() {
return ""
}
return f.Value.YNode().Value
}
// SetName sets the metadata name field.
func (rn *RNode) SetName(name string) error {
return rn.SetMapField(NewScalarRNode(name), MetadataField, NameField)
}
// GetNamespace gets the metadata namespace field.
func (rn *RNode) GetNamespace() (string, error) {
meta, err := rn.GetMeta()
@ -752,7 +780,7 @@ func (rn *RNode) GetValidatedMetadata() (ResourceMeta, error) {
return m, nil
}
// MatchesAnnotationSelector implements ifc.Kunstructured.
// MatchesAnnotationSelector returns true on a selector match to annotations.
func (rn *RNode) MatchesAnnotationSelector(selector string) (bool, error) {
s, err := labels.Parse(selector)
if err != nil {
@ -765,7 +793,7 @@ func (rn *RNode) MatchesAnnotationSelector(selector string) (bool, error) {
return s.Matches(labels.Set(slice)), nil
}
// MatchesLabelSelector implements ifc.Kunstructured.
// MatchesLabelSelector returns true on a selector match to labels.
func (rn *RNode) MatchesLabelSelector(selector string) (bool, error) {
s, err := labels.Parse(selector)
if err != nil {
@ -780,9 +808,8 @@ func (rn *RNode) MatchesLabelSelector(selector string) (bool, error) {
// HasNilEntryInList returns true if the RNode contains a list which has
// a nil item, along with the path to the missing item.
// TODO(broken): This was copied from
// api/k8sdeps/kunstruct/factory.go//checkListItemNil
// and doesn't do what it claims to do (see TODO in unit test and pr 1513).
// TODO(broken): This doesn't do what it claims to do.
// (see TODO in unit test and pr 1513).
func (rn *RNode) HasNilEntryInList() (bool, string) {
return hasNilEntryInList(rn.value)
}
@ -859,3 +886,123 @@ func checkKey(key string, elems []*Node) bool {
}
return count == len(elems)
}
// Deprecated: use pipes instead.
// GetSlice returns the contents of the slice field at the given path.
func (rn *RNode) GetSlice(path string) ([]interface{}, error) {
value, err := rn.GetFieldValue(path)
if err != nil {
return nil, err
}
if sliceValue, ok := value.([]interface{}); ok {
return sliceValue, nil
}
return nil, fmt.Errorf("node %s is not a slice", path)
}
// Deprecated: use pipes instead.
// GetString returns the contents of the string field at the given path.
func (rn *RNode) GetString(path string) (string, error) {
value, err := rn.GetFieldValue(path)
if err != nil {
return "", err
}
if v, ok := value.(string); ok {
return v, nil
}
return "", fmt.Errorf("node %s is not a string: %v", path, value)
}
// Deprecated: use slash paths instead.
// GetFieldValue finds period delimited fields.
// TODO: When doing kustomize var replacement, which is likely a
// a primary use of this function and the reason it returns interface{}
// rather than string, we do conversion from Nodes to Go types and back
// to nodes. We should figure out how to do replacement using raw nodes,
// assuming we keep the var feature in kustomize.
// The other end of this is: refvar.go:updateNodeValue.
func (rn *RNode) GetFieldValue(path string) (interface{}, error) {
fields := convertSliceIndex(strings.Split(path, "."))
rn, err := rn.Pipe(Lookup(fields...))
if err != nil {
return nil, err
}
if rn == nil {
return nil, NoFieldError{path}
}
yn := rn.YNode()
// If this is an alias node, resolve it
if yn.Kind == yaml.AliasNode {
yn = yn.Alias
}
// Return value as map for DocumentNode and MappingNode kinds
if yn.Kind == yaml.DocumentNode || yn.Kind == yaml.MappingNode {
var result map[string]interface{}
if err := yn.Decode(&result); err != nil {
return nil, err
}
return result, err
}
// Return value as slice for SequenceNode kind
if yn.Kind == yaml.SequenceNode {
var result []interface{}
if err := yn.Decode(&result); err != nil {
return nil, err
}
return result, nil
}
if yn.Kind != yaml.ScalarNode {
return nil, fmt.Errorf("expected ScalarNode, got Kind=%d", yn.Kind)
}
switch yn.Tag {
case NodeTagString:
return yn.Value, nil
case NodeTagInt:
return strconv.Atoi(yn.Value)
case NodeTagFloat:
return strconv.ParseFloat(yn.Value, 64)
case NodeTagBool:
return strconv.ParseBool(yn.Value)
default:
// Possibly this should be an error or log.
return yn.Value, nil
}
}
// convertSliceIndex traverses the items in `fields` and find
// if there is a slice index in the item and change it to a
// valid Lookup field path. For example, 'ports[0]' will be
// converted to 'ports' and '0'.
func convertSliceIndex(fields []string) []string {
var res []string
for _, s := range fields {
if !strings.HasSuffix(s, "]") {
res = append(res, s)
continue
}
re := regexp.MustCompile(`^(.*)\[(\d+)\]$`)
groups := re.FindStringSubmatch(s)
if len(groups) == 0 {
// no match, add to result
res = append(res, s)
continue
}
if groups[1] != "" {
res = append(res, groups[1])
}
res = append(res, groups[2])
}
return res
}
type NoFieldError struct {
Field string
}
func (e NoFieldError) Error() string {
return fmt.Sprintf("no field named '%s'", e.Field)
}

View File

@ -53,14 +53,27 @@ func appendListNode(dst, src *yaml.RNode, keys []string) (*yaml.RNode, error) {
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
}
@ -97,6 +110,47 @@ func validateKeys(valuesList [][]string, values []string, keys []string) ([]stri
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
@ -105,6 +159,9 @@ func (l *Walker) setAssociativeSequenceElements(valuesList [][]string, keys []st
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:
@ -114,12 +171,14 @@ func (l *Walker) setAssociativeSequenceElements(valuesList [][]string, keys []st
// 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)
validKeys, validValues = validateKeys(valuesList, values, keys)
val, err := Walker{
VisitKeysAsScalars: l.VisitKeysAsScalars,
InferAssociativeLists: l.InferAssociativeLists,
@ -144,7 +203,7 @@ func (l *Walker) setAssociativeSequenceElements(valuesList [][]string, keys []st
return nil, err
}
exit = true
} else if val.Field(key) == nil {
} 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 {
@ -162,7 +221,7 @@ func (l *Walker) setAssociativeSequenceElements(valuesList [][]string, keys []st
_, err = itemsToBeAdded.Pipe(yaml.ElementSetter{
Element: val.YNode(),
Keys: validKeys,
Values: values,
Values: validValues,
})
if err != nil {
return nil, err
@ -171,7 +230,6 @@ func (l *Walker) setAssociativeSequenceElements(valuesList [][]string, keys []st
var err error
if len(valuesList) > 0 {
validKeys, _ := validateKeys(valuesList, valuesList[0], keys)
if l.MergeOptions.ListIncreaseDirection == yaml.MergeOptionsListPrepend {
// items from patches are needed to be prepended. so we append the
// dest to itemsToBeAdded
@ -314,6 +372,7 @@ func (l Walker) elementPrimitiveValues() [][]string {
// 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 {