mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
Merge pull request #110405 from neolit123/1.25-kubelet-config-patches
kubeadm: add support for patching a "kubeletconfiguration" target
This commit is contained in:
commit
cd4b8ed070
@ -421,8 +421,8 @@ type HostPathMount struct {
|
|||||||
type Patches struct {
|
type Patches struct {
|
||||||
// Directory is a path to a directory that contains files named "target[suffix][+patchtype].extension".
|
// Directory is a path to a directory that contains files named "target[suffix][+patchtype].extension".
|
||||||
// For example, "kube-apiserver0+merge.yaml" or just "etcd.json". "target" can be one of
|
// For example, "kube-apiserver0+merge.yaml" or just "etcd.json". "target" can be one of
|
||||||
// "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd". "patchtype" can be one
|
// "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration".
|
||||||
// of "strategic" "merge" or "json" and they match the patch formats supported by kubectl.
|
// "patchtype" can be one of "strategic" "merge" or "json" and they match the patch formats supported by kubectl.
|
||||||
// The default "patchtype" is "strategic". "extension" must be either "json" or "yaml".
|
// The default "patchtype" is "strategic". "extension" must be either "json" or "yaml".
|
||||||
// "suffix" is an optional string that can be used to determine which patches are applied
|
// "suffix" is an optional string that can be used to determine which patches are applied
|
||||||
// first alpha-numerically.
|
// first alpha-numerically.
|
||||||
|
@ -435,8 +435,8 @@ type HostPathMount struct {
|
|||||||
type Patches struct {
|
type Patches struct {
|
||||||
// Directory is a path to a directory that contains files named "target[suffix][+patchtype].extension".
|
// Directory is a path to a directory that contains files named "target[suffix][+patchtype].extension".
|
||||||
// For example, "kube-apiserver0+merge.yaml" or just "etcd.json". "target" can be one of
|
// For example, "kube-apiserver0+merge.yaml" or just "etcd.json". "target" can be one of
|
||||||
// "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd". "patchtype" can be one
|
// "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration".
|
||||||
// of "strategic" "merge" or "json" and they match the patch formats supported by kubectl.
|
// "patchtype" can be one of "strategic" "merge" or "json" and they match the patch formats supported by kubectl.
|
||||||
// The default "patchtype" is "strategic". "extension" must be either "json" or "yaml".
|
// The default "patchtype" is "strategic". "extension" must be either "json" or "yaml".
|
||||||
// "suffix" is an optional string that can be used to determine which patches are applied
|
// "suffix" is an optional string that can be used to determine which patches are applied
|
||||||
// first alpha-numerically.
|
// first alpha-numerically.
|
||||||
|
@ -96,7 +96,7 @@ func AddPatchesFlag(fs *pflag.FlagSet, patchesDir *string) {
|
|||||||
const usage = `Path to a directory that contains files named ` +
|
const usage = `Path to a directory that contains files named ` +
|
||||||
`"target[suffix][+patchtype].extension". For example, ` +
|
`"target[suffix][+patchtype].extension". For example, ` +
|
||||||
`"kube-apiserver0+merge.yaml" or just "etcd.json". ` +
|
`"kube-apiserver0+merge.yaml" or just "etcd.json". ` +
|
||||||
`"target" can be one of "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd". ` +
|
`"target" can be one of "kube-apiserver", "kube-controller-manager", "kube-scheduler", "etcd", "kubeletconfiguration". ` +
|
||||||
`"patchtype" can be one of "strategic", "merge" or "json" and they match the patch formats ` +
|
`"patchtype" can be one of "strategic", "merge" or "json" and they match the patch formats ` +
|
||||||
`supported by kubectl. The default "patchtype" is "strategic". "extension" must be either ` +
|
`supported by kubectl. The default "patchtype" is "strategic". "extension" must be either ` +
|
||||||
`"json" or "yaml". "suffix" is an optional string that can be used to determine ` +
|
`"json" or "yaml". "suffix" is an optional string that can be used to determine ` +
|
||||||
|
@ -48,6 +48,7 @@ func NewKubeletStartPhase() workflow.Phase {
|
|||||||
options.CfgPath,
|
options.CfgPath,
|
||||||
options.NodeCRISocket,
|
options.NodeCRISocket,
|
||||||
options.NodeName,
|
options.NodeName,
|
||||||
|
options.Patches,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,7 +75,7 @@ func runKubeletStart(c workflow.RunData) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write the kubelet configuration file to disk.
|
// Write the kubelet configuration file to disk.
|
||||||
if err := kubeletphase.WriteConfigToDisk(&data.Cfg().ClusterConfiguration, data.KubeletDir()); err != nil {
|
if err := kubeletphase.WriteConfigToDisk(&data.Cfg().ClusterConfiguration, data.KubeletDir(), data.PatchesDir(), data.OutputWriter()); err != nil {
|
||||||
return errors.Wrap(err, "error writing kubelet configuration to disk")
|
return errors.Wrap(err, "error writing kubelet configuration to disk")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +76,7 @@ func NewKubeletStartPhase() workflow.Phase {
|
|||||||
options.TokenDiscoverySkipCAHash,
|
options.TokenDiscoverySkipCAHash,
|
||||||
options.TLSBootstrapToken,
|
options.TLSBootstrapToken,
|
||||||
options.TokenStr,
|
options.TokenStr,
|
||||||
|
options.Patches,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,7 +175,7 @@ func runKubeletStartJoinPhase(c workflow.RunData) (returnErr error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write the configuration for the kubelet (using the bootstrap token credentials) to disk so the kubelet can start
|
// Write the configuration for the kubelet (using the bootstrap token credentials) to disk so the kubelet can start
|
||||||
if err := kubeletphase.WriteConfigToDisk(&initCfg.ClusterConfiguration, data.KubeletDir()); err != nil {
|
if err := kubeletphase.WriteConfigToDisk(&initCfg.ClusterConfiguration, data.KubeletDir(), data.PatchesDir(), data.OutputWriter()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
|
|
||||||
@ -35,4 +37,5 @@ type Data interface {
|
|||||||
IgnorePreflightErrors() sets.String
|
IgnorePreflightErrors() sets.String
|
||||||
PatchesDir() string
|
PatchesDir() string
|
||||||
KubeConfigPath() string
|
KubeConfigPath() string
|
||||||
|
OutputWriter() io.Writer
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ func NewKubeletConfigPhase() workflow.Phase {
|
|||||||
InheritFlags: []string{
|
InheritFlags: []string{
|
||||||
options.DryRun,
|
options.DryRun,
|
||||||
options.KubeconfigPath,
|
options.KubeconfigPath,
|
||||||
|
options.Patches,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return phase
|
return phase
|
||||||
@ -73,7 +74,7 @@ func runKubeletConfigPhase() func(c workflow.RunData) error {
|
|||||||
// TODO: Checkpoint the current configuration first so that if something goes wrong it can be recovered
|
// TODO: Checkpoint the current configuration first so that if something goes wrong it can be recovered
|
||||||
|
|
||||||
// Store the kubelet component configuration.
|
// Store the kubelet component configuration.
|
||||||
if err = kubeletphase.WriteConfigToDisk(&cfg.ClusterConfiguration, kubeletDir); err != nil {
|
if err = kubeletphase.WriteConfigToDisk(&cfg.ClusterConfiguration, kubeletDir, data.PatchesDir(), data.OutputWriter()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ func runApply(flags *applyFlags, args []string) error {
|
|||||||
|
|
||||||
// Upgrade RBAC rules and addons.
|
// Upgrade RBAC rules and addons.
|
||||||
klog.V(1).Infoln("[upgrade/postupgrade] upgrading RBAC rules and addons")
|
klog.V(1).Infoln("[upgrade/postupgrade] upgrading RBAC rules and addons")
|
||||||
if err := upgrade.PerformPostUpgradeTasks(client, cfg, flags.dryRun); err != nil {
|
if err := upgrade.PerformPostUpgradeTasks(client, cfg, flags.patchesDir, flags.dryRun, flags.applyPlanFlags.out); err != nil {
|
||||||
return errors.Wrap(err, "[upgrade/postupgrade] FATAL post-upgrade error")
|
return errors.Wrap(err, "[upgrade/postupgrade] FATAL post-upgrade error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package upgrade
|
package upgrade
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -63,10 +64,11 @@ type nodeData struct {
|
|||||||
patchesDir string
|
patchesDir string
|
||||||
ignorePreflightErrors sets.String
|
ignorePreflightErrors sets.String
|
||||||
kubeConfigPath string
|
kubeConfigPath string
|
||||||
|
outputWriter io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
// newCmdNode returns the cobra command for `kubeadm upgrade node`
|
// newCmdNode returns the cobra command for `kubeadm upgrade node`
|
||||||
func newCmdNode() *cobra.Command {
|
func newCmdNode(out io.Writer) *cobra.Command {
|
||||||
nodeOptions := newNodeOptions()
|
nodeOptions := newNodeOptions()
|
||||||
nodeRunner := workflow.NewRunner()
|
nodeRunner := workflow.NewRunner()
|
||||||
|
|
||||||
@ -92,7 +94,7 @@ func newCmdNode() *cobra.Command {
|
|||||||
// sets the data builder function, that will be used by the runner
|
// sets the data builder function, that will be used by the runner
|
||||||
// both when running the entire workflow or single phases
|
// both when running the entire workflow or single phases
|
||||||
nodeRunner.SetDataInitializer(func(cmd *cobra.Command, args []string) (workflow.RunData, error) {
|
nodeRunner.SetDataInitializer(func(cmd *cobra.Command, args []string) (workflow.RunData, error) {
|
||||||
return newNodeData(cmd, args, nodeOptions)
|
return newNodeData(cmd, args, nodeOptions, out)
|
||||||
})
|
})
|
||||||
|
|
||||||
// binds the Runner to kubeadm upgrade node command by altering
|
// binds the Runner to kubeadm upgrade node command by altering
|
||||||
@ -123,7 +125,7 @@ func addUpgradeNodeFlags(flagSet *flag.FlagSet, nodeOptions *nodeOptions) {
|
|||||||
// newNodeData returns a new nodeData struct to be used for the execution of the kubeadm upgrade node workflow.
|
// newNodeData returns a new nodeData struct to be used for the execution of the kubeadm upgrade node workflow.
|
||||||
// This func takes care of validating nodeOptions passed to the command, and then it converts
|
// This func takes care of validating nodeOptions passed to the command, and then it converts
|
||||||
// options into the internal InitConfiguration type that is used as input all the phases in the kubeadm upgrade node workflow
|
// options into the internal InitConfiguration type that is used as input all the phases in the kubeadm upgrade node workflow
|
||||||
func newNodeData(cmd *cobra.Command, args []string, options *nodeOptions) (*nodeData, error) {
|
func newNodeData(cmd *cobra.Command, args []string, options *nodeOptions, out io.Writer) (*nodeData, error) {
|
||||||
client, err := getClient(options.kubeConfigPath, options.dryRun)
|
client, err := getClient(options.kubeConfigPath, options.dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", options.kubeConfigPath)
|
return nil, errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", options.kubeConfigPath)
|
||||||
@ -168,6 +170,7 @@ func newNodeData(cmd *cobra.Command, args []string, options *nodeOptions) (*node
|
|||||||
patchesDir: options.patchesDir,
|
patchesDir: options.patchesDir,
|
||||||
ignorePreflightErrors: ignorePreflightErrorsSet,
|
ignorePreflightErrors: ignorePreflightErrorsSet,
|
||||||
kubeConfigPath: options.kubeConfigPath,
|
kubeConfigPath: options.kubeConfigPath,
|
||||||
|
outputWriter: out,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,3 +218,7 @@ func (d *nodeData) IgnorePreflightErrors() sets.String {
|
|||||||
func (d *nodeData) KubeConfigPath() string {
|
func (d *nodeData) KubeConfigPath() string {
|
||||||
return d.kubeConfigPath
|
return d.kubeConfigPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *nodeData) OutputWriter() io.Writer {
|
||||||
|
return d.outputWriter
|
||||||
|
}
|
||||||
|
@ -60,7 +60,7 @@ func NewCmdUpgrade(out io.Writer) *cobra.Command {
|
|||||||
cmd.AddCommand(newCmdApply(flags))
|
cmd.AddCommand(newCmdApply(flags))
|
||||||
cmd.AddCommand(newCmdPlan(flags))
|
cmd.AddCommand(newCmdPlan(flags))
|
||||||
cmd.AddCommand(newCmdDiff(out))
|
cmd.AddCommand(newCmdDiff(out))
|
||||||
cmd.AddCommand(newCmdNode())
|
cmd.AddCommand(newCmdNode(out))
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ package kubelet
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
@ -27,16 +28,18 @@ import (
|
|||||||
rbac "k8s.io/api/rbac/v1"
|
rbac "k8s.io/api/rbac/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
|
kubeletconfig "k8s.io/kubelet/config/v1beta1"
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
|
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
|
||||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/patches"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WriteConfigToDisk writes the kubelet config object down to a file
|
// WriteConfigToDisk writes the kubelet config object down to a file
|
||||||
// Used at "kubeadm init" and "kubeadm upgrade" time
|
// Used at "kubeadm init" and "kubeadm upgrade" time
|
||||||
func WriteConfigToDisk(cfg *kubeadmapi.ClusterConfiguration, kubeletDir string) error {
|
func WriteConfigToDisk(cfg *kubeadmapi.ClusterConfiguration, kubeletDir, patchesDir string, output io.Writer) error {
|
||||||
kubeletCfg, ok := cfg.ComponentConfigs[componentconfigs.KubeletGroup]
|
kubeletCfg, ok := cfg.ComponentConfigs[componentconfigs.KubeletGroup]
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("no kubelet component config found")
|
return errors.New("no kubelet component config found")
|
||||||
@ -51,6 +54,25 @@ func WriteConfigToDisk(cfg *kubeadmapi.ClusterConfiguration, kubeletDir string)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply patches to the KubeletConfiguration
|
||||||
|
if len(patchesDir) != 0 {
|
||||||
|
target := "kubeletconfiguration"
|
||||||
|
knownTargets := []string{target}
|
||||||
|
patchManager, err := patches.GetPatchManagerForPath(patchesDir, knownTargets, output)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
patchTarget := &patches.PatchTarget{
|
||||||
|
Name: target,
|
||||||
|
StrategicMergePatchObject: kubeletconfig.KubeletConfiguration{},
|
||||||
|
Data: kubeletBytes,
|
||||||
|
}
|
||||||
|
if err := patchManager.ApplyPatchesToTarget(patchTarget); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
kubeletBytes = patchTarget.Data
|
||||||
|
}
|
||||||
|
|
||||||
return writeConfigBytesToDisk(kubeletBytes, kubeletDir)
|
return writeConfigBytesToDisk(kubeletBytes, kubeletDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ package upgrade
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -48,7 +49,7 @@ import (
|
|||||||
|
|
||||||
// PerformPostUpgradeTasks runs nearly the same functions as 'kubeadm init' would do
|
// PerformPostUpgradeTasks runs nearly the same functions as 'kubeadm init' would do
|
||||||
// Note that the mark-control-plane phase is left out, not needed, and no token is created as that doesn't belong to the upgrade
|
// Note that the mark-control-plane phase is left out, not needed, and no token is created as that doesn't belong to the upgrade
|
||||||
func PerformPostUpgradeTasks(client clientset.Interface, cfg *kubeadmapi.InitConfiguration, dryRun bool) error {
|
func PerformPostUpgradeTasks(client clientset.Interface, cfg *kubeadmapi.InitConfiguration, patchesDir string, dryRun bool, out io.Writer) error {
|
||||||
errs := []error{}
|
errs := []error{}
|
||||||
|
|
||||||
// Upload currently used configuration to the cluster
|
// Upload currently used configuration to the cluster
|
||||||
@ -64,7 +65,7 @@ func PerformPostUpgradeTasks(client clientset.Interface, cfg *kubeadmapi.InitCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write the new kubelet config down to disk and the env file if needed
|
// Write the new kubelet config down to disk and the env file if needed
|
||||||
if err := writeKubeletConfigFiles(client, cfg, dryRun); err != nil {
|
if err := writeKubeletConfigFiles(client, cfg, patchesDir, dryRun, out); err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +159,7 @@ func PerformPostUpgradeTasks(client clientset.Interface, cfg *kubeadmapi.InitCon
|
|||||||
return errorsutil.NewAggregate(errs)
|
return errorsutil.NewAggregate(errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeKubeletConfigFiles(client clientset.Interface, cfg *kubeadmapi.InitConfiguration, dryRun bool) error {
|
func writeKubeletConfigFiles(client clientset.Interface, cfg *kubeadmapi.InitConfiguration, patchesDir string, dryRun bool, out io.Writer) error {
|
||||||
kubeletDir, err := GetKubeletDir(dryRun)
|
kubeletDir, err := GetKubeletDir(dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// The error here should never occur in reality, would only be thrown if /tmp doesn't exist on the machine.
|
// The error here should never occur in reality, would only be thrown if /tmp doesn't exist on the machine.
|
||||||
@ -166,7 +167,7 @@ func writeKubeletConfigFiles(client clientset.Interface, cfg *kubeadmapi.InitCon
|
|||||||
}
|
}
|
||||||
errs := []error{}
|
errs := []error{}
|
||||||
// Write the configuration for the kubelet down to disk so the upgraded kubelet can start with fresh config
|
// Write the configuration for the kubelet down to disk so the upgraded kubelet can start with fresh config
|
||||||
if err := kubeletphase.WriteConfigToDisk(&cfg.ClusterConfiguration, kubeletDir); err != nil {
|
if err := kubeletphase.WriteConfigToDisk(&cfg.ClusterConfiguration, kubeletDir, patchesDir, out); err != nil {
|
||||||
errs = append(errs, errors.Wrap(err, "error writing kubelet configuration to file"))
|
errs = append(errs, errors.Wrap(err, "error writing kubelet configuration to file"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ import (
|
|||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -35,6 +35,7 @@ var testKnownTargets = []string{
|
|||||||
"kube-apiserver",
|
"kube-apiserver",
|
||||||
"kube-controller-manager",
|
"kube-controller-manager",
|
||||||
"kube-scheduler",
|
"kube-scheduler",
|
||||||
|
"kubeletconfiguration",
|
||||||
}
|
}
|
||||||
|
|
||||||
const testDirPattern = "patch-files"
|
const testDirPattern = "patch-files"
|
||||||
@ -312,6 +313,21 @@ func TestGetPatchManagerForPath(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "valid: kubeletconfiguration target is patched with json patch",
|
||||||
|
patchTarget: &PatchTarget{
|
||||||
|
Name: "kubeletconfiguration",
|
||||||
|
StrategicMergePatchObject: nil,
|
||||||
|
Data: []byte("foo: bar\n"),
|
||||||
|
},
|
||||||
|
expectedData: []byte(`{"foo":"zzz"}`),
|
||||||
|
files: []*file{
|
||||||
|
{
|
||||||
|
name: "kubeletconfiguration+json.json",
|
||||||
|
data: `[{"op": "replace", "path": "/foo", "value": "zzz"}]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "valid: kube-apiserver target is patched with strategic merge patch",
|
name: "valid: kube-apiserver target is patched with strategic merge patch",
|
||||||
patchTarget: &PatchTarget{
|
patchTarget: &PatchTarget{
|
||||||
|
Loading…
Reference in New Issue
Block a user