mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 13:37:30 +00:00
remove todo: switch UpdatePodSpecForObject to work on v1.PodSpec, use info.VersionedObject, and avoid conversion completely
This commit is contained in:
parent
4793b714d8
commit
dc312142ab
@ -29,6 +29,7 @@ go_library(
|
|||||||
"//pkg/kubectl/resource:go_default_library",
|
"//pkg/kubectl/resource:go_default_library",
|
||||||
"//pkg/kubectl/util/i18n:go_default_library",
|
"//pkg/kubectl/util/i18n:go_default_library",
|
||||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
@ -38,6 +39,7 @@ go_library(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -61,7 +63,6 @@ go_test(
|
|||||||
deps = [
|
deps = [
|
||||||
"//pkg/api/legacyscheme:go_default_library",
|
"//pkg/api/legacyscheme:go_default_library",
|
||||||
"//pkg/api/testapi:go_default_library",
|
"//pkg/api/testapi:go_default_library",
|
||||||
"//pkg/apis/apps:go_default_library",
|
|
||||||
"//pkg/apis/batch:go_default_library",
|
"//pkg/apis/batch:go_default_library",
|
||||||
"//pkg/apis/core:go_default_library",
|
"//pkg/apis/core:go_default_library",
|
||||||
"//pkg/apis/extensions:go_default_library",
|
"//pkg/apis/extensions:go_default_library",
|
||||||
@ -69,9 +70,16 @@ go_test(
|
|||||||
"//pkg/kubectl/cmd/testing:go_default_library",
|
"//pkg/kubectl/cmd/testing:go_default_library",
|
||||||
"//pkg/kubectl/cmd/util:go_default_library",
|
"//pkg/kubectl/cmd/util:go_default_library",
|
||||||
"//pkg/kubectl/resource:go_default_library",
|
"//pkg/kubectl/resource:go_default_library",
|
||||||
|
"//pkg/kubectl/scheme:go_default_library",
|
||||||
"//pkg/printers:go_default_library",
|
"//pkg/printers:go_default_library",
|
||||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/apps/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/apps/v1beta2:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/batch/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
|
@ -21,20 +21,19 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||||
)
|
)
|
||||||
|
|
||||||
// selectContainers allows one or more containers to be matched against a string or wildcard
|
// selectContainers allows one or more containers to be matched against a string or wildcard
|
||||||
func selectContainers(containers []api.Container, spec string) ([]*api.Container, []*api.Container) {
|
func selectContainers(containers []v1.Container, spec string) ([]*v1.Container, []*v1.Container) {
|
||||||
out := []*api.Container{}
|
out := []*v1.Container{}
|
||||||
skipped := []*api.Container{}
|
skipped := []*v1.Container{}
|
||||||
for i, c := range containers {
|
for i, c := range containers {
|
||||||
if selectString(c.Name, spec) {
|
if selectString(c.Name, spec) {
|
||||||
out = append(out, &containers[i])
|
out = append(out, &containers[i])
|
||||||
@ -127,10 +126,14 @@ type patchFn func(*resource.Info) ([]byte, error)
|
|||||||
// the changes in the object. Encoder must be able to encode the info into the appropriate destination type.
|
// the changes in the object. Encoder must be able to encode the info into the appropriate destination type.
|
||||||
// This function returns whether the mutation function made any change in the original object.
|
// This function returns whether the mutation function made any change in the original object.
|
||||||
func CalculatePatch(patch *Patch, encoder runtime.Encoder, mutateFn patchFn) bool {
|
func CalculatePatch(patch *Patch, encoder runtime.Encoder, mutateFn patchFn) bool {
|
||||||
versionedEncoder := legacyscheme.Codecs.EncoderForVersion(encoder, patch.Info.Mapping.GroupVersionKind.GroupVersion())
|
versioned, err := patch.Info.Mapping.ConvertToVersion(patch.Info.Object, patch.Info.Mapping.GroupVersionKind.GroupVersion())
|
||||||
|
if err != nil {
|
||||||
patch.Before, patch.Err = runtime.Encode(versionedEncoder, patch.Info.Object)
|
patch.Err = err
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
patch.Info.VersionedObject = versioned
|
||||||
|
|
||||||
|
patch.Before, patch.Err = runtime.Encode(encoder, patch.Info.VersionedObject)
|
||||||
patch.After, patch.Err = mutateFn(patch.Info)
|
patch.After, patch.Err = mutateFn(patch.Info)
|
||||||
if patch.Err != nil {
|
if patch.Err != nil {
|
||||||
return true
|
return true
|
||||||
@ -139,14 +142,7 @@ func CalculatePatch(patch *Patch, encoder runtime.Encoder, mutateFn patchFn) boo
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should be via New
|
patch.Patch, patch.Err = strategicpatch.CreateTwoWayMergePatch(patch.Before, patch.After, patch.Info.VersionedObject)
|
||||||
versioned, err := patch.Info.Mapping.ConvertToVersion(patch.Info.Object, patch.Info.Mapping.GroupVersionKind.GroupVersion())
|
|
||||||
if err != nil {
|
|
||||||
patch.Err = err
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
patch.Patch, patch.Err = strategicpatch.CreateTwoWayMergePatch(patch.Before, patch.After, versioned)
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,17 +159,17 @@ func CalculatePatches(infos []*resource.Info, encoder runtime.Encoder, mutateFn
|
|||||||
return patches
|
return patches
|
||||||
}
|
}
|
||||||
|
|
||||||
func findEnv(env []api.EnvVar, name string) (api.EnvVar, bool) {
|
func findEnv(env []v1.EnvVar, name string) (v1.EnvVar, bool) {
|
||||||
for _, e := range env {
|
for _, e := range env {
|
||||||
if e.Name == name {
|
if e.Name == name {
|
||||||
return e, true
|
return e, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.EnvVar{}, false
|
return v1.EnvVar{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateEnv(existing []api.EnvVar, env []api.EnvVar, remove []string) []api.EnvVar {
|
func updateEnv(existing []v1.EnvVar, env []v1.EnvVar, remove []string) []v1.EnvVar {
|
||||||
out := []api.EnvVar{}
|
out := []v1.EnvVar{}
|
||||||
covered := sets.NewString(remove...)
|
covered := sets.NewString(remove...)
|
||||||
for _, e := range existing {
|
for _, e := range existing {
|
||||||
if covered.Has(e.Name) {
|
if covered.Has(e.Name) {
|
||||||
|
@ -25,11 +25,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/client-go/kubernetes"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
|
||||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
envutil "k8s.io/kubernetes/pkg/kubectl/cmd/util/env"
|
envutil "k8s.io/kubernetes/pkg/kubectl/cmd/util/env"
|
||||||
@ -116,14 +116,13 @@ type EnvOptions struct {
|
|||||||
Prefix string
|
Prefix string
|
||||||
|
|
||||||
Mapper meta.RESTMapper
|
Mapper meta.RESTMapper
|
||||||
Typer runtime.ObjectTyper
|
|
||||||
Builder *resource.Builder
|
Builder *resource.Builder
|
||||||
Infos []*resource.Info
|
Infos []*resource.Info
|
||||||
Encoder runtime.Encoder
|
Encoder runtime.Encoder
|
||||||
|
|
||||||
Cmd *cobra.Command
|
Cmd *cobra.Command
|
||||||
|
|
||||||
UpdatePodSpecForObject func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error)
|
UpdatePodSpecForObject func(obj runtime.Object, fn func(*v1.PodSpec) error) (bool, error)
|
||||||
PrintObject func(cmd *cobra.Command, isLocal bool, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error
|
PrintObject func(cmd *cobra.Command, isLocal bool, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +162,7 @@ func NewCmdEnv(f cmdutil.Factory, in io.Reader, out, errout io.Writer) *cobra.Co
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateNoOverwrites(existing []api.EnvVar, env []api.EnvVar) error {
|
func validateNoOverwrites(existing []v1.EnvVar, env []v1.EnvVar) error {
|
||||||
for _, e := range env {
|
for _, e := range env {
|
||||||
if current, exists := findEnv(existing, e.Name); exists && current.Value != e.Value {
|
if current, exists := findEnv(existing, e.Name); exists && current.Value != e.Value {
|
||||||
return fmt.Errorf("'%s' already has a value (%s), and --overwrite is false", current.Name, current.Value)
|
return fmt.Errorf("'%s' already has a value (%s), and --overwrite is false", current.Name, current.Value)
|
||||||
@ -186,7 +185,7 @@ func (o *EnvOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
|
|||||||
return cmdutil.UsageErrorf(cmd, "one or more resources must be specified as <resource> <name> or <resource>/<name>")
|
return cmdutil.UsageErrorf(cmd, "one or more resources must be specified as <resource> <name> or <resource>/<name>")
|
||||||
}
|
}
|
||||||
|
|
||||||
o.Mapper, o.Typer = f.Object()
|
o.Mapper, _ = f.Object()
|
||||||
o.UpdatePodSpecForObject = f.UpdatePodSpecForObject
|
o.UpdatePodSpecForObject = f.UpdatePodSpecForObject
|
||||||
o.Encoder = f.JSONEncoder()
|
o.Encoder = f.JSONEncoder()
|
||||||
o.ContainerSelector = cmdutil.GetFlagString(cmd, "containers")
|
o.ContainerSelector = cmdutil.GetFlagString(cmd, "containers")
|
||||||
@ -216,10 +215,14 @@ func (o *EnvOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
|
|||||||
|
|
||||||
// RunEnv contains all the necessary functionality for the OpenShift cli env command
|
// RunEnv contains all the necessary functionality for the OpenShift cli env command
|
||||||
func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
|
func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
|
||||||
kubeClient, err := f.ClientSet()
|
var kubeClient *kubernetes.Clientset
|
||||||
|
if o.List {
|
||||||
|
client, err := f.KubernetesClientSet()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
kubeClient = client
|
||||||
|
}
|
||||||
|
|
||||||
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
|
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -253,14 +256,18 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, info := range infos {
|
for _, info := range infos {
|
||||||
switch from := info.Object.(type) {
|
versionedObject, err := info.Mapping.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion())
|
||||||
case *api.Secret:
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch from := versionedObject.(type) {
|
||||||
|
case *v1.Secret:
|
||||||
for key := range from.Data {
|
for key := range from.Data {
|
||||||
envVar := api.EnvVar{
|
envVar := v1.EnvVar{
|
||||||
Name: keyToEnvName(key),
|
Name: keyToEnvName(key),
|
||||||
ValueFrom: &api.EnvVarSource{
|
ValueFrom: &v1.EnvVarSource{
|
||||||
SecretKeyRef: &api.SecretKeySelector{
|
SecretKeyRef: &v1.SecretKeySelector{
|
||||||
LocalObjectReference: api.LocalObjectReference{
|
LocalObjectReference: v1.LocalObjectReference{
|
||||||
Name: from.Name,
|
Name: from.Name,
|
||||||
},
|
},
|
||||||
Key: key,
|
Key: key,
|
||||||
@ -269,13 +276,13 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
|
|||||||
}
|
}
|
||||||
env = append(env, envVar)
|
env = append(env, envVar)
|
||||||
}
|
}
|
||||||
case *api.ConfigMap:
|
case *v1.ConfigMap:
|
||||||
for key := range from.Data {
|
for key := range from.Data {
|
||||||
envVar := api.EnvVar{
|
envVar := v1.EnvVar{
|
||||||
Name: keyToEnvName(key),
|
Name: keyToEnvName(key),
|
||||||
ValueFrom: &api.EnvVarSource{
|
ValueFrom: &v1.EnvVarSource{
|
||||||
ConfigMapKeyRef: &api.ConfigMapKeySelector{
|
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
|
||||||
LocalObjectReference: api.LocalObjectReference{
|
LocalObjectReference: v1.LocalObjectReference{
|
||||||
Name: from.Name,
|
Name: from.Name,
|
||||||
},
|
},
|
||||||
Key: key,
|
Key: key,
|
||||||
@ -316,7 +323,7 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) ([]byte, error) {
|
patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) ([]byte, error) {
|
||||||
_, err := o.UpdatePodSpecForObject(info.Object, func(spec *api.PodSpec) error {
|
_, err := o.UpdatePodSpecForObject(info.VersionedObject, func(spec *v1.PodSpec) error {
|
||||||
resolutionErrorsEncountered := false
|
resolutionErrorsEncountered := false
|
||||||
containers, _ := selectContainers(spec.Containers, o.ContainerSelector)
|
containers, _ := selectContainers(spec.Containers, o.ContainerSelector)
|
||||||
if len(containers) == 0 {
|
if len(containers) == 0 {
|
||||||
@ -382,9 +389,7 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO: switch UpdatePodSpecForObject to work on v1.PodSpec, use info.VersionedObject, and avoid conversion completely
|
return runtime.Encode(o.Encoder, info.VersionedObject)
|
||||||
versionedEncoder := legacyscheme.Codecs.EncoderForVersion(o.Encoder, info.Mapping.GroupVersionKind.GroupVersion())
|
|
||||||
return runtime.Encode(versionedEncoder, info.Object)
|
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
})
|
})
|
||||||
@ -408,7 +413,7 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.PrintObject != nil && (o.Local || o.DryRun) {
|
if o.PrintObject != nil && (o.Local || o.DryRun) {
|
||||||
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, info.Object, o.Out); err != nil {
|
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, patch.Info.VersionedObject, o.Out); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@ -428,7 +433,11 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(o.Output) > 0 {
|
if len(o.Output) > 0 {
|
||||||
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, obj, o.Out); err != nil {
|
versionedObject, err := patch.Info.Mapping.ConvertToVersion(obj, patch.Info.Mapping.GroupVersionKind.GroupVersion())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, versionedObject, o.Out); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
|
@ -18,25 +18,37 @@ package set
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
|
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||||
|
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||||
|
batchv1 "k8s.io/api/batch/v1"
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/rest/fake"
|
"k8s.io/client-go/rest/fake"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
|
||||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||||
"k8s.io/kubernetes/pkg/printers"
|
"k8s.io/kubernetes/pkg/printers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSetEnvLocal(t *testing.T) {
|
func TestSetEnvLocal(t *testing.T) {
|
||||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||||
tf.Client = &fake.RESTClient{
|
tf.Client = &fake.RESTClient{
|
||||||
GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion,
|
GroupVersion: schema.GroupVersion{Version: ""},
|
||||||
NegotiatedSerializer: ns,
|
NegotiatedSerializer: ns,
|
||||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
||||||
@ -44,7 +56,7 @@ func TestSetEnvLocal(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
tf.Namespace = "test"
|
tf.Namespace = "test"
|
||||||
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion}}
|
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
|
||||||
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
cmd := NewCmdEnv(f, os.Stdin, buf, buf)
|
cmd := NewCmdEnv(f, os.Stdin, buf, buf)
|
||||||
@ -73,7 +85,7 @@ func TestSetEnvLocal(t *testing.T) {
|
|||||||
func TestSetMultiResourcesEnvLocal(t *testing.T) {
|
func TestSetMultiResourcesEnvLocal(t *testing.T) {
|
||||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||||
tf.Client = &fake.RESTClient{
|
tf.Client = &fake.RESTClient{
|
||||||
GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion,
|
GroupVersion: schema.GroupVersion{Version: ""},
|
||||||
NegotiatedSerializer: ns,
|
NegotiatedSerializer: ns,
|
||||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
||||||
@ -81,7 +93,7 @@ func TestSetMultiResourcesEnvLocal(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
tf.Namespace = "test"
|
tf.Namespace = "test"
|
||||||
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion}}
|
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
|
||||||
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
cmd := NewCmdEnv(f, os.Stdin, buf, buf)
|
cmd := NewCmdEnv(f, os.Stdin, buf, buf)
|
||||||
@ -108,3 +120,359 @@ func TestSetMultiResourcesEnvLocal(t *testing.T) {
|
|||||||
t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String())
|
t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetEnvRemote(t *testing.T) {
|
||||||
|
inputs := []struct {
|
||||||
|
object runtime.Object
|
||||||
|
apiPrefix, apiGroup, apiVersion string
|
||||||
|
testAPIGroup string
|
||||||
|
args []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
object: &extensionsv1beta1.ReplicaSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: extensionsv1beta1.ReplicaSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "extensions", apiVersion: "v1beta1",
|
||||||
|
args: []string{"replicaset", "nginx", "env=prod"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta2.ReplicaSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta2.ReplicaSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"replicaset", "nginx", "env=prod"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1.ReplicaSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1.ReplicaSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"replicaset", "nginx", "env=prod"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &extensionsv1beta1.DaemonSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: extensionsv1beta1.DaemonSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "extensions", apiVersion: "v1beta1",
|
||||||
|
args: []string{"daemonset", "nginx", "env=prod"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta2.DaemonSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta2.DaemonSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"daemonset", "nginx", "env=prod"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1.DaemonSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1.DaemonSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"daemonset", "nginx", "env=prod"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &extensionsv1beta1.Deployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: extensionsv1beta1.DeploymentSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "extensions", apiVersion: "v1beta1",
|
||||||
|
args: []string{"deployment", "nginx", "env=prod"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta1.Deployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta1.DeploymentSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta1",
|
||||||
|
args: []string{"deployment", "nginx", "env=prod"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta2.Deployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta2.DeploymentSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"deployment", "nginx", "env=prod"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1.Deployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1.DeploymentSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"deployment", "nginx", "env=prod"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta1.StatefulSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta1.StatefulSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "apps",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta1",
|
||||||
|
args: []string{"statefulset", "nginx", "env=prod"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta2.StatefulSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta2.StatefulSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "apps",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"statefulset", "nginx", "env=prod"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1.StatefulSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1.StatefulSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "apps",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"statefulset", "nginx", "env=prod"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &batchv1.Job{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: batchv1.JobSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "batch",
|
||||||
|
apiPrefix: "/apis", apiGroup: "batch", apiVersion: "v1",
|
||||||
|
args: []string{"job", "nginx", "env=prod"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &v1.ReplicationController{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: v1.ReplicationControllerSpec{
|
||||||
|
Template: &v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "",
|
||||||
|
apiPrefix: "/api", apiGroup: "", apiVersion: "v1",
|
||||||
|
args: []string{"replicationcontroller", "nginx", "env=prod"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, input := range inputs {
|
||||||
|
groupVersion := schema.GroupVersion{Group: input.apiGroup, Version: input.apiVersion}
|
||||||
|
testapi.Default = testapi.Groups[input.testAPIGroup]
|
||||||
|
f, tf, _, ns := cmdtesting.NewAPIFactory()
|
||||||
|
codec := scheme.Codecs.CodecForVersions(scheme.Codecs.LegacyCodec(groupVersion), scheme.Codecs.UniversalDecoder(groupVersion), groupVersion, groupVersion)
|
||||||
|
tf.Printer = printers.NewVersionedPrinter(&printers.YAMLPrinter{}, testapi.Default.Converter(), *testapi.Default.GroupVersion())
|
||||||
|
tf.Namespace = "test"
|
||||||
|
tf.CategoryExpander = resource.LegacyCategoryExpander
|
||||||
|
tf.Client = &fake.RESTClient{
|
||||||
|
GroupVersion: groupVersion,
|
||||||
|
NegotiatedSerializer: ns,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", tf.Namespace, input.args[1])
|
||||||
|
switch p, m := req.URL.Path, req.Method; {
|
||||||
|
case p == resourcePath && m == http.MethodGet:
|
||||||
|
return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(codec, input.object)}, nil
|
||||||
|
case p == resourcePath && m == http.MethodPatch:
|
||||||
|
stream, err := req.GetBody()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bytes, err := ioutil.ReadAll(stream)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
assert.Contains(t, string(bytes), `"value":`+`"`+"prod"+`"`, fmt.Sprintf("env not updated for %#v", input.object))
|
||||||
|
return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(codec, input.object)}, nil
|
||||||
|
default:
|
||||||
|
t.Errorf("%s: unexpected request: %s %#v\n%#v", "image", req.Method, req.URL, req)
|
||||||
|
return nil, fmt.Errorf("unexpected request")
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
VersionedAPIPath: path.Join(input.apiPrefix, testapi.Default.GroupVersion().String()),
|
||||||
|
}
|
||||||
|
out := new(bytes.Buffer)
|
||||||
|
cmd := NewCmdEnv(f, out, out, out)
|
||||||
|
cmd.SetOutput(out)
|
||||||
|
cmd.Flags().Set("output", "yaml")
|
||||||
|
opts := EnvOptions{
|
||||||
|
Out: out,
|
||||||
|
Local: false}
|
||||||
|
err := opts.Complete(f, cmd, input.args)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = opts.RunEnv(f)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,12 +21,11 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
|
||||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||||
@ -39,9 +38,9 @@ type ImageOptions struct {
|
|||||||
resource.FilenameOptions
|
resource.FilenameOptions
|
||||||
|
|
||||||
Mapper meta.RESTMapper
|
Mapper meta.RESTMapper
|
||||||
Typer runtime.ObjectTyper
|
|
||||||
Infos []*resource.Info
|
Infos []*resource.Info
|
||||||
Encoder runtime.Encoder
|
Encoder runtime.Encoder
|
||||||
|
Decoder runtime.Decoder
|
||||||
Selector string
|
Selector string
|
||||||
Out io.Writer
|
Out io.Writer
|
||||||
Err io.Writer
|
Err io.Writer
|
||||||
@ -56,7 +55,7 @@ type ImageOptions struct {
|
|||||||
ResolveImage func(in string) (string, error)
|
ResolveImage func(in string) (string, error)
|
||||||
|
|
||||||
PrintObject func(cmd *cobra.Command, isLocal bool, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error
|
PrintObject func(cmd *cobra.Command, isLocal bool, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error
|
||||||
UpdatePodSpecForObject func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error)
|
UpdatePodSpecForObject func(obj runtime.Object, fn func(*v1.PodSpec) error) (bool, error)
|
||||||
Resources []string
|
Resources []string
|
||||||
ContainerImages map[string]string
|
ContainerImages map[string]string
|
||||||
}
|
}
|
||||||
@ -116,9 +115,10 @@ func NewCmdImage(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *ImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
func (o *ImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||||
o.Mapper, o.Typer = f.Object()
|
o.Mapper, _ = f.Object()
|
||||||
o.UpdatePodSpecForObject = f.UpdatePodSpecForObject
|
o.UpdatePodSpecForObject = f.UpdatePodSpecForObject
|
||||||
o.Encoder = f.JSONEncoder()
|
o.Encoder = f.JSONEncoder()
|
||||||
|
o.Decoder = f.Decoder(true)
|
||||||
o.ShortOutput = cmdutil.GetFlagString(cmd, "output") == "name"
|
o.ShortOutput = cmdutil.GetFlagString(cmd, "output") == "name"
|
||||||
o.Record = cmdutil.GetRecordFlag(cmd)
|
o.Record = cmdutil.GetRecordFlag(cmd)
|
||||||
o.ChangeCause = f.Command(cmd, false)
|
o.ChangeCause = f.Command(cmd, false)
|
||||||
@ -189,7 +189,7 @@ func (o *ImageOptions) Run() error {
|
|||||||
|
|
||||||
patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) ([]byte, error) {
|
patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) ([]byte, error) {
|
||||||
transformed := false
|
transformed := false
|
||||||
_, err := o.UpdatePodSpecForObject(info.Object, func(spec *api.PodSpec) error {
|
_, err := o.UpdatePodSpecForObject(info.VersionedObject, func(spec *v1.PodSpec) error {
|
||||||
for name, image := range o.ContainerImages {
|
for name, image := range o.ContainerImages {
|
||||||
var (
|
var (
|
||||||
containerFound bool
|
containerFound bool
|
||||||
@ -226,9 +226,7 @@ func (o *ImageOptions) Run() error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if transformed && err == nil {
|
if transformed && err == nil {
|
||||||
// TODO: switch UpdatePodSpecForObject to work on v1.PodSpec, use info.VersionedObject, and avoid conversion completely
|
return runtime.Encode(o.Encoder, info.VersionedObject)
|
||||||
versionedEncoder := legacyscheme.Codecs.EncoderForVersion(o.Encoder, info.Mapping.GroupVersionKind.GroupVersion())
|
|
||||||
return runtime.Encode(versionedEncoder, info.Object)
|
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
})
|
})
|
||||||
@ -246,7 +244,7 @@ func (o *ImageOptions) Run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.PrintObject != nil && (o.Local || o.DryRun) {
|
if o.PrintObject != nil && (o.Local || o.DryRun) {
|
||||||
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, info.Object, o.Out); err != nil {
|
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, patch.Info.VersionedObject, o.Out); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@ -272,7 +270,11 @@ func (o *ImageOptions) Run() error {
|
|||||||
info.Refresh(obj, true)
|
info.Refresh(obj, true)
|
||||||
|
|
||||||
if len(o.Output) > 0 {
|
if len(o.Output) > 0 {
|
||||||
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, obj, o.Out); err != nil {
|
versionedObject, err := patch.Info.Mapping.ConvertToVersion(obj, patch.Info.Mapping.GroupVersionKind.GroupVersion())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, versionedObject, o.Out); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
|
@ -18,24 +18,36 @@ package set
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
|
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||||
|
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||||
|
batchv1 "k8s.io/api/batch/v1"
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/rest/fake"
|
"k8s.io/client-go/rest/fake"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
|
||||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||||
"k8s.io/kubernetes/pkg/printers"
|
"k8s.io/kubernetes/pkg/printers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestImageLocal(t *testing.T) {
|
func TestImageLocal(t *testing.T) {
|
||||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||||
tf.Client = &fake.RESTClient{
|
tf.Client = &fake.RESTClient{
|
||||||
GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion,
|
GroupVersion: schema.GroupVersion{Version: ""},
|
||||||
NegotiatedSerializer: ns,
|
NegotiatedSerializer: ns,
|
||||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
||||||
@ -43,7 +55,7 @@ func TestImageLocal(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
tf.Namespace = "test"
|
tf.Namespace = "test"
|
||||||
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion}}
|
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
|
||||||
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
cmd := NewCmdImage(f, buf, buf)
|
cmd := NewCmdImage(f, buf, buf)
|
||||||
@ -138,7 +150,7 @@ func TestSetImageValidation(t *testing.T) {
|
|||||||
func TestSetMultiResourcesImageLocal(t *testing.T) {
|
func TestSetMultiResourcesImageLocal(t *testing.T) {
|
||||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||||
tf.Client = &fake.RESTClient{
|
tf.Client = &fake.RESTClient{
|
||||||
GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion,
|
GroupVersion: schema.GroupVersion{Version: ""},
|
||||||
NegotiatedSerializer: ns,
|
NegotiatedSerializer: ns,
|
||||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
||||||
@ -146,7 +158,7 @@ func TestSetMultiResourcesImageLocal(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
tf.Namespace = "test"
|
tf.Namespace = "test"
|
||||||
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion}}
|
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
|
||||||
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
cmd := NewCmdImage(f, buf, buf)
|
cmd := NewCmdImage(f, buf, buf)
|
||||||
@ -175,3 +187,359 @@ func TestSetMultiResourcesImageLocal(t *testing.T) {
|
|||||||
t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String())
|
t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetImageRemote(t *testing.T) {
|
||||||
|
inputs := []struct {
|
||||||
|
object runtime.Object
|
||||||
|
apiPrefix, apiGroup, apiVersion string
|
||||||
|
testAPIGroup string
|
||||||
|
args []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
object: &extensionsv1beta1.ReplicaSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: extensionsv1beta1.ReplicaSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "extensions", apiVersion: "v1beta1",
|
||||||
|
args: []string{"replicaset", "nginx", "*=thingy"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta2.ReplicaSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta2.ReplicaSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"replicaset", "nginx", "*=thingy"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1.ReplicaSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1.ReplicaSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"replicaset", "nginx", "*=thingy"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &extensionsv1beta1.DaemonSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: extensionsv1beta1.DaemonSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "extensions", apiVersion: "v1beta1",
|
||||||
|
args: []string{"daemonset", "nginx", "*=thingy"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta2.DaemonSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta2.DaemonSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"daemonset", "nginx", "*=thingy"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1.DaemonSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1.DaemonSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"daemonset", "nginx", "*=thingy"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &extensionsv1beta1.Deployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: extensionsv1beta1.DeploymentSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "extensions", apiVersion: "v1beta1",
|
||||||
|
args: []string{"deployment", "nginx", "*=thingy"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta1.Deployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta1.DeploymentSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta1",
|
||||||
|
args: []string{"deployment", "nginx", "*=thingy"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta2.Deployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta2.DeploymentSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"deployment", "nginx", "*=thingy"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1.Deployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1.DeploymentSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"deployment", "nginx", "*=thingy"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta1.StatefulSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta1.StatefulSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "apps",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta1",
|
||||||
|
args: []string{"statefulset", "nginx", "*=thingy"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta2.StatefulSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta2.StatefulSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "apps",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"statefulset", "nginx", "*=thingy"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1.StatefulSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1.StatefulSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "apps",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"statefulset", "nginx", "*=thingy"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &batchv1.Job{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: batchv1.JobSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "batch",
|
||||||
|
apiPrefix: "/apis", apiGroup: "batch", apiVersion: "v1",
|
||||||
|
args: []string{"job", "nginx", "*=thingy"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &v1.ReplicationController{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: v1.ReplicationControllerSpec{
|
||||||
|
Template: &v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "",
|
||||||
|
apiPrefix: "/api", apiGroup: "", apiVersion: "v1",
|
||||||
|
args: []string{"replicationcontroller", "nginx", "*=thingy"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, input := range inputs {
|
||||||
|
groupVersion := schema.GroupVersion{Group: input.apiGroup, Version: input.apiVersion}
|
||||||
|
testapi.Default = testapi.Groups[input.testAPIGroup]
|
||||||
|
f, tf, _, ns := cmdtesting.NewAPIFactory()
|
||||||
|
codec := scheme.Codecs.CodecForVersions(scheme.Codecs.LegacyCodec(groupVersion), scheme.Codecs.UniversalDecoder(groupVersion), groupVersion, groupVersion)
|
||||||
|
tf.Printer = printers.NewVersionedPrinter(&printers.YAMLPrinter{}, testapi.Default.Converter(), *testapi.Default.GroupVersion())
|
||||||
|
tf.Namespace = "test"
|
||||||
|
tf.CategoryExpander = resource.LegacyCategoryExpander
|
||||||
|
tf.Client = &fake.RESTClient{
|
||||||
|
GroupVersion: groupVersion,
|
||||||
|
NegotiatedSerializer: ns,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", tf.Namespace, input.args[1])
|
||||||
|
switch p, m := req.URL.Path, req.Method; {
|
||||||
|
case p == resourcePath && m == http.MethodGet:
|
||||||
|
return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(codec, input.object)}, nil
|
||||||
|
case p == resourcePath && m == http.MethodPatch:
|
||||||
|
stream, err := req.GetBody()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bytes, err := ioutil.ReadAll(stream)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
assert.Contains(t, string(bytes), `"image":`+`"`+"thingy"+`"`, fmt.Sprintf("image not updated for %#v", input.object))
|
||||||
|
return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(codec, input.object)}, nil
|
||||||
|
default:
|
||||||
|
t.Errorf("%s: unexpected request: %s %#v\n%#v", "image", req.Method, req.URL, req)
|
||||||
|
return nil, fmt.Errorf("unexpected request")
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
VersionedAPIPath: path.Join(input.apiPrefix, testapi.Default.GroupVersion().String()),
|
||||||
|
}
|
||||||
|
out := new(bytes.Buffer)
|
||||||
|
cmd := NewCmdImage(f, out, out)
|
||||||
|
cmd.SetOutput(out)
|
||||||
|
cmd.Flags().Set("output", "yaml")
|
||||||
|
opts := ImageOptions{
|
||||||
|
Out: out,
|
||||||
|
Local: false}
|
||||||
|
err := opts.Complete(f, cmd, input.args)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = opts.Run()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -22,13 +22,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
|
||||||
"k8s.io/kubernetes/pkg/kubectl"
|
"k8s.io/kubernetes/pkg/kubectl"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
@ -64,7 +63,6 @@ type ResourcesOptions struct {
|
|||||||
resource.FilenameOptions
|
resource.FilenameOptions
|
||||||
|
|
||||||
Mapper meta.RESTMapper
|
Mapper meta.RESTMapper
|
||||||
Typer runtime.ObjectTyper
|
|
||||||
Infos []*resource.Info
|
Infos []*resource.Info
|
||||||
Encoder runtime.Encoder
|
Encoder runtime.Encoder
|
||||||
Out io.Writer
|
Out io.Writer
|
||||||
@ -80,10 +78,10 @@ type ResourcesOptions struct {
|
|||||||
|
|
||||||
Limits string
|
Limits string
|
||||||
Requests string
|
Requests string
|
||||||
ResourceRequirements api.ResourceRequirements
|
ResourceRequirements v1.ResourceRequirements
|
||||||
|
|
||||||
PrintObject func(cmd *cobra.Command, isLocal bool, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error
|
PrintObject func(cmd *cobra.Command, isLocal bool, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error
|
||||||
UpdatePodSpecForObject func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error)
|
UpdatePodSpecForObject func(obj runtime.Object, fn func(*v1.PodSpec) error) (bool, error)
|
||||||
Resources []string
|
Resources []string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +126,7 @@ func NewCmdResources(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *ResourcesOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
func (o *ResourcesOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||||
o.Mapper, o.Typer = f.Object()
|
o.Mapper, _ = f.Object()
|
||||||
o.UpdatePodSpecForObject = f.UpdatePodSpecForObject
|
o.UpdatePodSpecForObject = f.UpdatePodSpecForObject
|
||||||
o.Encoder = f.JSONEncoder()
|
o.Encoder = f.JSONEncoder()
|
||||||
o.Output = cmdutil.GetFlagString(cmd, "output")
|
o.Output = cmdutil.GetFlagString(cmd, "output")
|
||||||
@ -180,7 +178,7 @@ func (o *ResourcesOptions) Validate() error {
|
|||||||
return fmt.Errorf("you must specify an update to requests or limits (in the form of --requests/--limits)")
|
return fmt.Errorf("you must specify an update to requests or limits (in the form of --requests/--limits)")
|
||||||
}
|
}
|
||||||
|
|
||||||
o.ResourceRequirements, err = kubectl.HandleResourceRequirements(map[string]string{"limits": o.Limits, "requests": o.Requests})
|
o.ResourceRequirements, err = kubectl.HandleResourceRequirementsV1(map[string]string{"limits": o.Limits, "requests": o.Requests})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -192,19 +190,19 @@ func (o *ResourcesOptions) Run() error {
|
|||||||
allErrs := []error{}
|
allErrs := []error{}
|
||||||
patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) ([]byte, error) {
|
patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) ([]byte, error) {
|
||||||
transformed := false
|
transformed := false
|
||||||
_, err := o.UpdatePodSpecForObject(info.Object, func(spec *api.PodSpec) error {
|
_, err := o.UpdatePodSpecForObject(info.VersionedObject, func(spec *v1.PodSpec) error {
|
||||||
containers, _ := selectContainers(spec.Containers, o.ContainerSelector)
|
containers, _ := selectContainers(spec.Containers, o.ContainerSelector)
|
||||||
if len(containers) != 0 {
|
if len(containers) != 0 {
|
||||||
for i := range containers {
|
for i := range containers {
|
||||||
if len(o.Limits) != 0 && len(containers[i].Resources.Limits) == 0 {
|
if len(o.Limits) != 0 && len(containers[i].Resources.Limits) == 0 {
|
||||||
containers[i].Resources.Limits = make(api.ResourceList)
|
containers[i].Resources.Limits = make(v1.ResourceList)
|
||||||
}
|
}
|
||||||
for key, value := range o.ResourceRequirements.Limits {
|
for key, value := range o.ResourceRequirements.Limits {
|
||||||
containers[i].Resources.Limits[key] = value
|
containers[i].Resources.Limits[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(o.Requests) != 0 && len(containers[i].Resources.Requests) == 0 {
|
if len(o.Requests) != 0 && len(containers[i].Resources.Requests) == 0 {
|
||||||
containers[i].Resources.Requests = make(api.ResourceList)
|
containers[i].Resources.Requests = make(v1.ResourceList)
|
||||||
}
|
}
|
||||||
for key, value := range o.ResourceRequirements.Requests {
|
for key, value := range o.ResourceRequirements.Requests {
|
||||||
containers[i].Resources.Requests[key] = value
|
containers[i].Resources.Requests[key] = value
|
||||||
@ -217,9 +215,7 @@ func (o *ResourcesOptions) Run() error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if transformed && err == nil {
|
if transformed && err == nil {
|
||||||
// TODO: switch UpdatePodSpecForObject to work on v1.PodSpec, use info.VersionedObject, and avoid conversion completely
|
return runtime.Encode(o.Encoder, info.VersionedObject)
|
||||||
versionedEncoder := legacyscheme.Codecs.EncoderForVersion(o.Encoder, info.Mapping.GroupVersionKind.GroupVersion())
|
|
||||||
return runtime.Encode(versionedEncoder, info.Object)
|
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
})
|
})
|
||||||
@ -238,7 +234,7 @@ func (o *ResourcesOptions) Run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.Local || cmdutil.GetDryRunFlag(o.Cmd) {
|
if o.Local || cmdutil.GetDryRunFlag(o.Cmd) {
|
||||||
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, info.Object, o.Out); err != nil {
|
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, patch.Info.VersionedObject, o.Out); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@ -263,7 +259,14 @@ func (o *ResourcesOptions) Run() error {
|
|||||||
|
|
||||||
shortOutput := o.Output == "name"
|
shortOutput := o.Output == "name"
|
||||||
if len(o.Output) > 0 && !shortOutput {
|
if len(o.Output) > 0 && !shortOutput {
|
||||||
return o.PrintObject(o.Cmd, o.Local, o.Mapper, info.Object, o.Out)
|
versionedObject, err := patch.Info.Mapping.ConvertToVersion(obj, patch.Info.Mapping.GroupVersionKind.GroupVersion())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, versionedObject, o.Out); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
cmdutil.PrintSuccess(o.Mapper, shortOutput, o.Out, info.Mapping.Resource, info.Name, false, "resource requirements updated")
|
cmdutil.PrintSuccess(o.Mapper, shortOutput, o.Out, info.Mapping.Resource, info.Name, false, "resource requirements updated")
|
||||||
}
|
}
|
||||||
|
@ -18,24 +18,36 @@ package set
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
|
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||||
|
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||||
|
batchv1 "k8s.io/api/batch/v1"
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/rest/fake"
|
"k8s.io/client-go/rest/fake"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
|
||||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||||
"k8s.io/kubernetes/pkg/printers"
|
"k8s.io/kubernetes/pkg/printers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestResourcesLocal(t *testing.T) {
|
func TestResourcesLocal(t *testing.T) {
|
||||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||||
tf.Client = &fake.RESTClient{
|
tf.Client = &fake.RESTClient{
|
||||||
GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion,
|
GroupVersion: schema.GroupVersion{Version: ""},
|
||||||
NegotiatedSerializer: ns,
|
NegotiatedSerializer: ns,
|
||||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
||||||
@ -43,7 +55,7 @@ func TestResourcesLocal(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
tf.Namespace = "test"
|
tf.Namespace = "test"
|
||||||
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion}}
|
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
|
||||||
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
cmd := NewCmdResources(f, buf, buf)
|
cmd := NewCmdResources(f, buf, buf)
|
||||||
@ -79,7 +91,7 @@ func TestResourcesLocal(t *testing.T) {
|
|||||||
func TestSetMultiResourcesLimitsLocal(t *testing.T) {
|
func TestSetMultiResourcesLimitsLocal(t *testing.T) {
|
||||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||||
tf.Client = &fake.RESTClient{
|
tf.Client = &fake.RESTClient{
|
||||||
GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion,
|
GroupVersion: schema.GroupVersion{Version: ""},
|
||||||
NegotiatedSerializer: ns,
|
NegotiatedSerializer: ns,
|
||||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
||||||
@ -87,7 +99,7 @@ func TestSetMultiResourcesLimitsLocal(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
tf.Namespace = "test"
|
tf.Namespace = "test"
|
||||||
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion}}
|
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
|
||||||
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
cmd := NewCmdResources(f, buf, buf)
|
cmd := NewCmdResources(f, buf, buf)
|
||||||
@ -120,3 +132,366 @@ func TestSetMultiResourcesLimitsLocal(t *testing.T) {
|
|||||||
t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String())
|
t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetResourcesRemote(t *testing.T) {
|
||||||
|
inputs := []struct {
|
||||||
|
object runtime.Object
|
||||||
|
apiPrefix, apiGroup, apiVersion string
|
||||||
|
testAPIGroup string
|
||||||
|
args []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
object: &extensionsv1beta1.ReplicaSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: extensionsv1beta1.ReplicaSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "extensions", apiVersion: "v1beta1",
|
||||||
|
args: []string{"replicaset", "nginx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta2.ReplicaSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta2.ReplicaSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"replicaset", "nginx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1.ReplicaSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1.ReplicaSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"replicaset", "nginx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &extensionsv1beta1.DaemonSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: extensionsv1beta1.DaemonSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "extensions", apiVersion: "v1beta1",
|
||||||
|
args: []string{"daemonset", "nginx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta2.DaemonSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta2.DaemonSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"daemonset", "nginx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1.DaemonSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1.DaemonSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"daemonset", "nginx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &extensionsv1beta1.Deployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: extensionsv1beta1.DeploymentSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "extensions", apiVersion: "v1beta1",
|
||||||
|
args: []string{"deployment", "nginx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta1.Deployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta1.DeploymentSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta1",
|
||||||
|
args: []string{"deployment", "nginx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta2.Deployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta2.DeploymentSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"deployment", "nginx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1.Deployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1.DeploymentSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"deployment", "nginx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta1.StatefulSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta1.StatefulSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "apps",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta1",
|
||||||
|
args: []string{"statefulset", "nginx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta2.StatefulSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta2.StatefulSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "apps",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"statefulset", "nginx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1.StatefulSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1.StatefulSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "apps",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"statefulset", "nginx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &batchv1.Job{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: batchv1.JobSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "batch",
|
||||||
|
apiPrefix: "/apis", apiGroup: "batch", apiVersion: "v1",
|
||||||
|
args: []string{"job", "nginx"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &v1.ReplicationController{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: v1.ReplicationControllerSpec{
|
||||||
|
Template: &v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "",
|
||||||
|
apiPrefix: "/api", apiGroup: "", apiVersion: "v1",
|
||||||
|
args: []string{"replicationcontroller", "nginx"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, input := range inputs {
|
||||||
|
groupVersion := schema.GroupVersion{Group: input.apiGroup, Version: input.apiVersion}
|
||||||
|
testapi.Default = testapi.Groups[input.testAPIGroup]
|
||||||
|
f, tf, _, ns := cmdtesting.NewAPIFactory()
|
||||||
|
codec := scheme.Codecs.CodecForVersions(scheme.Codecs.LegacyCodec(groupVersion), scheme.Codecs.UniversalDecoder(groupVersion), groupVersion, groupVersion)
|
||||||
|
mapper, typer := f.Object()
|
||||||
|
tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{testapi.Default.Codec()}, Typer: typer, Mapper: mapper}
|
||||||
|
tf.Namespace = "test"
|
||||||
|
tf.CategoryExpander = resource.LegacyCategoryExpander
|
||||||
|
tf.Client = &fake.RESTClient{
|
||||||
|
GroupVersion: groupVersion,
|
||||||
|
NegotiatedSerializer: ns,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", tf.Namespace, input.args[1])
|
||||||
|
switch p, m := req.URL.Path, req.Method; {
|
||||||
|
case p == resourcePath && m == http.MethodGet:
|
||||||
|
return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(codec, input.object)}, nil
|
||||||
|
case p == resourcePath && m == http.MethodPatch:
|
||||||
|
stream, err := req.GetBody()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bytes, err := ioutil.ReadAll(stream)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
assert.Contains(t, string(bytes), "200m", fmt.Sprintf("resources not updated for %#v", input.object))
|
||||||
|
return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(codec, input.object)}, nil
|
||||||
|
default:
|
||||||
|
t.Errorf("%s: unexpected request: %s %#v\n%#v", "resources", req.Method, req.URL, req)
|
||||||
|
return nil, fmt.Errorf("unexpected request")
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
VersionedAPIPath: path.Join(input.apiPrefix, testapi.Default.GroupVersion().String()),
|
||||||
|
}
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
cmd := NewCmdResources(f, buf, buf)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
cmd.Flags().Set("output", "yaml")
|
||||||
|
opts := ResourcesOptions{
|
||||||
|
Out: buf,
|
||||||
|
Local: true,
|
||||||
|
Limits: "cpu=200m,memory=512Mi",
|
||||||
|
ContainerSelector: "*"}
|
||||||
|
err := opts.Complete(f, cmd, input.args)
|
||||||
|
if err == nil {
|
||||||
|
err = opts.Validate()
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
err = opts.Run()
|
||||||
|
}
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -23,11 +23,11 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
|
||||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||||
@ -61,14 +61,15 @@ type serviceAccountConfig struct {
|
|||||||
out io.Writer
|
out io.Writer
|
||||||
err io.Writer
|
err io.Writer
|
||||||
dryRun bool
|
dryRun bool
|
||||||
|
cmd *cobra.Command
|
||||||
shortOutput bool
|
shortOutput bool
|
||||||
all bool
|
all bool
|
||||||
record bool
|
record bool
|
||||||
output string
|
output string
|
||||||
changeCause string
|
changeCause string
|
||||||
local bool
|
local bool
|
||||||
saPrint func(obj runtime.Object) error
|
PrintObject func(cmd *cobra.Command, isLocal bool, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error
|
||||||
updatePodSpecForObject func(runtime.Object, func(*api.PodSpec) error) (bool, error)
|
updatePodSpecForObject func(runtime.Object, func(*v1.PodSpec) error) (bool, error)
|
||||||
infos []*resource.Info
|
infos []*resource.Info
|
||||||
serviceAccountName string
|
serviceAccountName string
|
||||||
}
|
}
|
||||||
@ -113,9 +114,9 @@ func (saConfig *serviceAccountConfig) Complete(f cmdutil.Factory, cmd *cobra.Com
|
|||||||
saConfig.dryRun = cmdutil.GetDryRunFlag(cmd)
|
saConfig.dryRun = cmdutil.GetDryRunFlag(cmd)
|
||||||
saConfig.output = cmdutil.GetFlagString(cmd, "output")
|
saConfig.output = cmdutil.GetFlagString(cmd, "output")
|
||||||
saConfig.updatePodSpecForObject = f.UpdatePodSpecForObject
|
saConfig.updatePodSpecForObject = f.UpdatePodSpecForObject
|
||||||
saConfig.saPrint = func(obj runtime.Object) error {
|
saConfig.PrintObject = f.PrintObject
|
||||||
return f.PrintObject(cmd, saConfig.local, saConfig.mapper, obj, saConfig.out)
|
saConfig.cmd = cmd
|
||||||
}
|
|
||||||
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
|
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -148,11 +149,11 @@ func (saConfig *serviceAccountConfig) Complete(f cmdutil.Factory, cmd *cobra.Com
|
|||||||
func (saConfig *serviceAccountConfig) Run() error {
|
func (saConfig *serviceAccountConfig) Run() error {
|
||||||
patchErrs := []error{}
|
patchErrs := []error{}
|
||||||
patchFn := func(info *resource.Info) ([]byte, error) {
|
patchFn := func(info *resource.Info) ([]byte, error) {
|
||||||
saConfig.updatePodSpecForObject(info.Object, func(podSpec *api.PodSpec) error {
|
saConfig.updatePodSpecForObject(info.VersionedObject, func(podSpec *v1.PodSpec) error {
|
||||||
podSpec.ServiceAccountName = saConfig.serviceAccountName
|
podSpec.ServiceAccountName = saConfig.serviceAccountName
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return runtime.Encode(saConfig.encoder, info.Object)
|
return runtime.Encode(saConfig.encoder, info.VersionedObject)
|
||||||
}
|
}
|
||||||
patches := CalculatePatches(saConfig.infos, saConfig.encoder, patchFn)
|
patches := CalculatePatches(saConfig.infos, saConfig.encoder, patchFn)
|
||||||
for _, patch := range patches {
|
for _, patch := range patches {
|
||||||
@ -162,7 +163,9 @@ func (saConfig *serviceAccountConfig) Run() error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if saConfig.local || saConfig.dryRun {
|
if saConfig.local || saConfig.dryRun {
|
||||||
saConfig.saPrint(patch.Info.Object)
|
if err := saConfig.PrintObject(saConfig.cmd, saConfig.local, saConfig.mapper, patch.Info.VersionedObject, saConfig.out); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
patched, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch)
|
patched, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch)
|
||||||
@ -179,7 +182,14 @@ func (saConfig *serviceAccountConfig) Run() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(saConfig.output) > 0 {
|
if len(saConfig.output) > 0 {
|
||||||
saConfig.saPrint(patched)
|
versionedObject, err := patch.Info.Mapping.ConvertToVersion(patched, patch.Info.Mapping.GroupVersionKind.GroupVersion())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := saConfig.PrintObject(saConfig.cmd, saConfig.local, saConfig.mapper, versionedObject, saConfig.out); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
cmdutil.PrintSuccess(saConfig.mapper, saConfig.shortOutput, saConfig.out, info.Mapping.Resource, info.Name, saConfig.dryRun, "serviceaccount updated")
|
cmdutil.PrintSuccess(saConfig.mapper, saConfig.shortOutput, saConfig.out, info.Mapping.Resource, info.Name, saConfig.dryRun, "serviceaccount updated")
|
||||||
}
|
}
|
||||||
|
@ -26,19 +26,21 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
|
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||||
|
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||||
|
batchv1 "k8s.io/api/batch/v1"
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
restclient "k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/rest/fake"
|
"k8s.io/client-go/rest/fake"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
"k8s.io/kubernetes/pkg/apis/apps"
|
|
||||||
"k8s.io/kubernetes/pkg/apis/batch"
|
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
|
||||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||||
"k8s.io/kubernetes/pkg/printers"
|
"k8s.io/kubernetes/pkg/printers"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -51,22 +53,21 @@ Example resource specifications include:
|
|||||||
'<resource> <name>'
|
'<resource> <name>'
|
||||||
'<resource>'`
|
'<resource>'`
|
||||||
|
|
||||||
func TestServiceAccountLocal(t *testing.T) {
|
func TestSetServiceAccountLocal(t *testing.T) {
|
||||||
inputs := []struct {
|
inputs := []struct {
|
||||||
yaml string
|
yaml string
|
||||||
apiGroup string
|
apiGroup string
|
||||||
}{
|
}{
|
||||||
{yaml: "../../../../test/fixtures/doc-yaml/user-guide/replication.yaml", apiGroup: api.GroupName},
|
{yaml: "../../../../test/fixtures/doc-yaml/user-guide/replication.yaml", apiGroup: ""},
|
||||||
{yaml: "../../../../test/fixtures/doc-yaml/admin/daemon.yaml", apiGroup: extensions.GroupName},
|
{yaml: "../../../../test/fixtures/doc-yaml/admin/daemon.yaml", apiGroup: "extensions"},
|
||||||
{yaml: "../../../../test/fixtures/doc-yaml/user-guide/replicaset/redis-slave.yaml", apiGroup: extensions.GroupName},
|
{yaml: "../../../../test/fixtures/doc-yaml/user-guide/replicaset/redis-slave.yaml", apiGroup: "extensions"},
|
||||||
{yaml: "../../../../test/fixtures/doc-yaml/user-guide/job.yaml", apiGroup: batch.GroupName},
|
{yaml: "../../../../test/fixtures/doc-yaml/user-guide/job.yaml", apiGroup: "batch"},
|
||||||
{yaml: "../../../../test/fixtures/doc-yaml/user-guide/deployment.yaml", apiGroup: extensions.GroupName},
|
{yaml: "../../../../test/fixtures/doc-yaml/user-guide/deployment.yaml", apiGroup: "extensions"},
|
||||||
{yaml: "../../../../examples/storage/minio/minio-distributed-statefulset.yaml", apiGroup: apps.GroupName},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f, tf, _, _ := cmdtesting.NewAPIFactory()
|
f, tf, _, _ := cmdtesting.NewAPIFactory()
|
||||||
tf.Client = &fake.RESTClient{
|
tf.Client = &fake.RESTClient{
|
||||||
GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion,
|
GroupVersion: schema.GroupVersion{Version: "v1"},
|
||||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -93,71 +94,232 @@ func TestServiceAccountLocal(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceAccountRemote(t *testing.T) {
|
func TestSetServiceAccountMultiLocal(t *testing.T) {
|
||||||
|
testapi.Default = testapi.Groups[""]
|
||||||
|
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||||
|
tf.Client = &fake.RESTClient{
|
||||||
|
GroupVersion: schema.GroupVersion{Version: ""},
|
||||||
|
NegotiatedSerializer: ns,
|
||||||
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
|
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
tf.Namespace = "test"
|
||||||
|
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
cmd := NewCmdServiceAccount(f, buf, buf)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
cmd.Flags().Set("output", "name")
|
||||||
|
cmd.Flags().Set("local", "true")
|
||||||
|
mapper, typer := f.Object()
|
||||||
|
tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer, Mapper: mapper}
|
||||||
|
opts := serviceAccountConfig{fileNameOptions: resource.FilenameOptions{
|
||||||
|
Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}},
|
||||||
|
out: buf,
|
||||||
|
local: true}
|
||||||
|
|
||||||
|
err := opts.Complete(f, cmd, []string{serviceAccount})
|
||||||
|
if err == nil {
|
||||||
|
err = opts.Run()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
expectedOut := "replicationcontrollers/first-rc\nreplicationcontrollers/second-rc\n"
|
||||||
|
if buf.String() != expectedOut {
|
||||||
|
t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetServiceAccountRemote(t *testing.T) {
|
||||||
inputs := []struct {
|
inputs := []struct {
|
||||||
object runtime.Object
|
object runtime.Object
|
||||||
apiPrefix, apiGroup string
|
apiPrefix, apiGroup, apiVersion string
|
||||||
|
testAPIGroup string
|
||||||
args []string
|
args []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
object: &extensions.ReplicaSet{
|
object: &extensionsv1beta1.ReplicaSet{
|
||||||
TypeMeta: metav1.TypeMeta{Kind: "ReplicaSet", APIVersion: legacyscheme.Registry.GroupOrDie(extensions.GroupName).GroupVersion.String()},
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
},
|
},
|
||||||
apiPrefix: "/apis", apiGroup: extensions.GroupName,
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "extensions", apiVersion: "v1beta1",
|
||||||
args: []string{"replicaset", "nginx", serviceAccount},
|
args: []string{"replicaset", "nginx", serviceAccount},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
object: &extensions.DaemonSet{
|
object: &appsv1beta2.ReplicaSet{
|
||||||
TypeMeta: metav1.TypeMeta{Kind: "DaemonSet", APIVersion: legacyscheme.Registry.GroupOrDie(extensions.GroupName).GroupVersion.String()},
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1beta2.ReplicaSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"replicaset", "nginx", serviceAccount},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1.ReplicaSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1.ReplicaSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"replicaset", "nginx", serviceAccount},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &extensionsv1beta1.DaemonSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
},
|
},
|
||||||
apiPrefix: "/apis", apiGroup: extensions.GroupName,
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "extensions", apiVersion: "v1beta1",
|
||||||
args: []string{"daemonset", "nginx", serviceAccount},
|
args: []string{"daemonset", "nginx", serviceAccount},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
object: &api.ReplicationController{
|
object: &appsv1beta2.DaemonSet{
|
||||||
TypeMeta: metav1.TypeMeta{Kind: "ReplicationController", APIVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion.String()},
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
},
|
},
|
||||||
apiPrefix: "/api", apiGroup: api.GroupName,
|
testAPIGroup: "extensions",
|
||||||
args: []string{"replicationcontroller", "nginx", serviceAccount}},
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"daemonset", "nginx", serviceAccount},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
object: &extensions.Deployment{
|
object: &appsv1.DaemonSet{
|
||||||
TypeMeta: metav1.TypeMeta{Kind: "Deployment", APIVersion: legacyscheme.Registry.GroupOrDie(extensions.GroupName).GroupVersion.String()},
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
},
|
},
|
||||||
apiPrefix: "/apis", apiGroup: extensions.GroupName,
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"daemonset", "nginx", serviceAccount},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &extensionsv1beta1.Deployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "extensions", apiVersion: "v1beta1",
|
||||||
args: []string{"deployment", "nginx", serviceAccount},
|
args: []string{"deployment", "nginx", serviceAccount},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
object: &batch.Job{
|
object: &appsv1beta1.Deployment{
|
||||||
TypeMeta: metav1.TypeMeta{Kind: "Job", APIVersion: legacyscheme.Registry.GroupOrDie(batch.GroupName).GroupVersion.String()},
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
},
|
},
|
||||||
apiPrefix: "/apis", apiGroup: batch.GroupName,
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta1",
|
||||||
|
args: []string{"deployment", "nginx", serviceAccount},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta2.Deployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"deployment", "nginx", serviceAccount},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1.Deployment{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1.DeploymentSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "extensions",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"deployment", "nginx", serviceAccount},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta1.StatefulSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
},
|
||||||
|
testAPIGroup: "apps",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta1",
|
||||||
|
args: []string{"statefulset", "nginx", serviceAccount},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1beta2.StatefulSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
},
|
||||||
|
testAPIGroup: "apps",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1beta2",
|
||||||
|
args: []string{"statefulset", "nginx", serviceAccount},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &appsv1.StatefulSet{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
Spec: appsv1.StatefulSetSpec{
|
||||||
|
Template: v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "nginx",
|
||||||
|
Image: "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAPIGroup: "apps",
|
||||||
|
apiPrefix: "/apis", apiGroup: "apps", apiVersion: "v1",
|
||||||
|
args: []string{"statefulset", "nginx", serviceAccount},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: &batchv1.Job{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
|
},
|
||||||
|
testAPIGroup: "batch",
|
||||||
|
apiPrefix: "/apis", apiGroup: "batch", apiVersion: "v1",
|
||||||
args: []string{"job", "nginx", serviceAccount},
|
args: []string{"job", "nginx", serviceAccount},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
object: &apps.StatefulSet{
|
object: &v1.ReplicationController{
|
||||||
TypeMeta: metav1.TypeMeta{Kind: "StatefulSet", APIVersion: legacyscheme.Registry.GroupOrDie(apps.GroupName).GroupVersion.String()},
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
|
||||||
},
|
},
|
||||||
apiPrefix: "/apis", apiGroup: apps.GroupName,
|
testAPIGroup: "",
|
||||||
args: []string{"statefulset", "nginx", serviceAccount},
|
apiPrefix: "/api", apiGroup: "", apiVersion: "v1",
|
||||||
|
args: []string{"replicationcontroller", "nginx", serviceAccount},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, input := range inputs {
|
for _, input := range inputs {
|
||||||
|
groupVersion := schema.GroupVersion{Group: input.apiGroup, Version: input.apiVersion}
|
||||||
groupVersion := legacyscheme.Registry.GroupOrDie(input.apiGroup).GroupVersion
|
testapi.Default = testapi.Groups[input.testAPIGroup]
|
||||||
testapi.Default = testapi.Groups[input.apiGroup]
|
f, tf, _, ns := cmdtesting.NewAPIFactory()
|
||||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
codec := scheme.Codecs.CodecForVersions(scheme.Codecs.LegacyCodec(groupVersion), scheme.Codecs.UniversalDecoder(groupVersion), groupVersion, groupVersion)
|
||||||
tf.Printer = printers.NewVersionedPrinter(&printers.YAMLPrinter{}, testapi.Default.Converter(), *testapi.Default.GroupVersion())
|
tf.Printer = printers.NewVersionedPrinter(&printers.YAMLPrinter{}, testapi.Default.Converter(), *testapi.Default.GroupVersion())
|
||||||
tf.Namespace = "test"
|
tf.Namespace = "test"
|
||||||
tf.CategoryExpander = resource.LegacyCategoryExpander
|
tf.CategoryExpander = resource.LegacyCategoryExpander
|
||||||
tf.Client = &fake.RESTClient{
|
tf.Client = &fake.RESTClient{
|
||||||
GroupVersion: legacyscheme.Registry.GroupOrDie(input.apiGroup).GroupVersion,
|
GroupVersion: groupVersion,
|
||||||
NegotiatedSerializer: testapi.Default.NegotiatedSerializer(),
|
NegotiatedSerializer: ns,
|
||||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", tf.Namespace, input.args[1])
|
resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", tf.Namespace, input.args[1])
|
||||||
switch p, m := req.URL.Path, req.Method; {
|
switch p, m := req.URL.Path, req.Method; {
|
||||||
@ -179,13 +341,12 @@ func TestServiceAccountRemote(t *testing.T) {
|
|||||||
return nil, fmt.Errorf("unexpected request")
|
return nil, fmt.Errorf("unexpected request")
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
VersionedAPIPath: path.Join(input.apiPrefix, groupVersion.String()),
|
VersionedAPIPath: path.Join(input.apiPrefix, testapi.Default.GroupVersion().String()),
|
||||||
}
|
}
|
||||||
out := new(bytes.Buffer)
|
out := new(bytes.Buffer)
|
||||||
cmd := NewCmdServiceAccount(f, out, out)
|
cmd := NewCmdServiceAccount(f, out, out)
|
||||||
cmd.SetOutput(out)
|
cmd.SetOutput(out)
|
||||||
cmd.Flags().Set("output", "yaml")
|
cmd.Flags().Set("output", "yaml")
|
||||||
|
|
||||||
saConfig := serviceAccountConfig{
|
saConfig := serviceAccountConfig{
|
||||||
out: out,
|
out: out,
|
||||||
local: false}
|
local: false}
|
||||||
|
@ -27,6 +27,7 @@ go_library(
|
|||||||
"//pkg/printers:go_default_library",
|
"//pkg/printers:go_default_library",
|
||||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
@ -448,7 +449,7 @@ func (f *FakeFactory) ApproximatePodTemplateForObject(obj runtime.Object) (*api.
|
|||||||
return f.ApproximatePodTemplateForObject(obj)
|
return f.ApproximatePodTemplateForObject(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FakeFactory) UpdatePodSpecForObject(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error) {
|
func (f *FakeFactory) UpdatePodSpecForObject(obj runtime.Object, fn func(*v1.PodSpec) error) (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,9 +48,13 @@ go_library(
|
|||||||
"//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library",
|
"//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library",
|
||||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/apps/v1:go_default_library",
|
||||||
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
|
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/apps/v1beta2:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/batch/v1:go_default_library",
|
||||||
"//vendor/k8s.io/api/batch/v2alpha1:go_default_library",
|
"//vendor/k8s.io/api/batch/v2alpha1:go_default_library",
|
||||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
6
pkg/kubectl/cmd/util/env/BUILD
vendored
6
pkg/kubectl/cmd/util/env/BUILD
vendored
@ -10,13 +10,13 @@ go_library(
|
|||||||
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/util/env",
|
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/util/env",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/api/resource:go_default_library",
|
"//pkg/api/v1/resource:go_default_library",
|
||||||
"//pkg/apis/core:go_default_library",
|
|
||||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
|
||||||
"//pkg/fieldpath:go_default_library",
|
"//pkg/fieldpath:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
16
pkg/kubectl/cmd/util/env/env_parse.go
vendored
16
pkg/kubectl/cmd/util/env/env_parse.go
vendored
@ -24,8 +24,8 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Env returns an environment variable if not nil, or a default value.
|
// Env returns an environment variable if not nil, or a default value.
|
||||||
@ -83,8 +83,8 @@ func SplitEnvironmentFromResources(args []string) (resources, envArgs []string,
|
|||||||
|
|
||||||
// parseIntoEnvVar parses the list of key-value pairs into kubernetes EnvVar.
|
// parseIntoEnvVar parses the list of key-value pairs into kubernetes EnvVar.
|
||||||
// envVarType is for making errors more specific to user intentions.
|
// envVarType is for making errors more specific to user intentions.
|
||||||
func parseIntoEnvVar(spec []string, defaultReader io.Reader, envVarType string) ([]api.EnvVar, []string, error) {
|
func parseIntoEnvVar(spec []string, defaultReader io.Reader, envVarType string) ([]v1.EnvVar, []string, error) {
|
||||||
env := []api.EnvVar{}
|
env := []v1.EnvVar{}
|
||||||
exists := sets.NewString()
|
exists := sets.NewString()
|
||||||
var remove []string
|
var remove []string
|
||||||
for _, envSpec := range spec {
|
for _, envSpec := range spec {
|
||||||
@ -106,7 +106,7 @@ func parseIntoEnvVar(spec []string, defaultReader io.Reader, envVarType string)
|
|||||||
return nil, nil, fmt.Errorf("invalid %s: %v", envVarType, envSpec)
|
return nil, nil, fmt.Errorf("invalid %s: %v", envVarType, envSpec)
|
||||||
}
|
}
|
||||||
exists.Insert(parts[0])
|
exists.Insert(parts[0])
|
||||||
env = append(env, api.EnvVar{
|
env = append(env, v1.EnvVar{
|
||||||
Name: parts[0],
|
Name: parts[0],
|
||||||
Value: parts[1],
|
Value: parts[1],
|
||||||
})
|
})
|
||||||
@ -126,12 +126,12 @@ func parseIntoEnvVar(spec []string, defaultReader io.Reader, envVarType string)
|
|||||||
|
|
||||||
// ParseEnv parses the elements of the first argument looking for environment variables in key=value form and, if one of those values is "-", it also scans the reader.
|
// ParseEnv parses the elements of the first argument looking for environment variables in key=value form and, if one of those values is "-", it also scans the reader.
|
||||||
// The same environment variable cannot be both modified and removed in the same command.
|
// The same environment variable cannot be both modified and removed in the same command.
|
||||||
func ParseEnv(spec []string, defaultReader io.Reader) ([]api.EnvVar, []string, error) {
|
func ParseEnv(spec []string, defaultReader io.Reader) ([]v1.EnvVar, []string, error) {
|
||||||
return parseIntoEnvVar(spec, defaultReader, "environment variable")
|
return parseIntoEnvVar(spec, defaultReader, "environment variable")
|
||||||
}
|
}
|
||||||
|
|
||||||
func readEnv(r io.Reader, envVarType string) ([]api.EnvVar, error) {
|
func readEnv(r io.Reader, envVarType string) ([]v1.EnvVar, error) {
|
||||||
env := []api.EnvVar{}
|
env := []v1.EnvVar{}
|
||||||
scanner := bufio.NewScanner(r)
|
scanner := bufio.NewScanner(r)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
envSpec := scanner.Text()
|
envSpec := scanner.Text()
|
||||||
@ -143,7 +143,7 @@ func readEnv(r io.Reader, envVarType string) ([]api.EnvVar, error) {
|
|||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
return nil, fmt.Errorf("invalid %s: %v", envVarType, envSpec)
|
return nil, fmt.Errorf("invalid %s: %v", envVarType, envSpec)
|
||||||
}
|
}
|
||||||
env = append(env, api.EnvVar{
|
env = append(env, v1.EnvVar{
|
||||||
Name: parts[0],
|
Name: parts[0],
|
||||||
Value: parts[1],
|
Value: parts[1],
|
||||||
})
|
})
|
||||||
|
2
pkg/kubectl/cmd/util/env/env_parse_test.go
vendored
2
pkg/kubectl/cmd/util/env/env_parse_test.go
vendored
@ -84,7 +84,7 @@ func ExampleParseEnv_good() {
|
|||||||
ss := []string{"ENV=VARIABLE", "AND=ANOTHER", "REMOVE-", "-"}
|
ss := []string{"ENV=VARIABLE", "AND=ANOTHER", "REMOVE-", "-"}
|
||||||
fmt.Println(ParseEnv(ss, r))
|
fmt.Println(ParseEnv(ss, r))
|
||||||
// Output:
|
// Output:
|
||||||
// [{ENV VARIABLE <nil>} {AND ANOTHER <nil>} {FROM READER <nil>}] [REMOVE] <nil>
|
// [{ENV VARIABLE nil} {AND ANOTHER nil} {FROM READER nil}] [REMOVE] <nil>
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleParseEnv_bad() {
|
func ExampleParseEnv_bad() {
|
||||||
|
26
pkg/kubectl/cmd/util/env/env_resolve.go
vendored
26
pkg/kubectl/cmd/util/env/env_resolve.go
vendored
@ -19,30 +19,30 @@ package env
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/api/resource"
|
"k8s.io/client-go/kubernetes"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
"k8s.io/kubernetes/pkg/api/v1/resource"
|
||||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
|
||||||
"k8s.io/kubernetes/pkg/fieldpath"
|
"k8s.io/kubernetes/pkg/fieldpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ResourceStore defines a new resource store data structure.
|
// ResourceStore defines a new resource store data structure.
|
||||||
type ResourceStore struct {
|
type ResourceStore struct {
|
||||||
SecretStore map[string]*api.Secret
|
SecretStore map[string]*v1.Secret
|
||||||
ConfigMapStore map[string]*api.ConfigMap
|
ConfigMapStore map[string]*v1.ConfigMap
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewResourceStore returns a pointer to a new resource store data structure.
|
// NewResourceStore returns a pointer to a new resource store data structure.
|
||||||
func NewResourceStore() *ResourceStore {
|
func NewResourceStore() *ResourceStore {
|
||||||
return &ResourceStore{
|
return &ResourceStore{
|
||||||
SecretStore: make(map[string]*api.Secret),
|
SecretStore: make(map[string]*v1.Secret),
|
||||||
ConfigMapStore: make(map[string]*api.ConfigMap),
|
ConfigMapStore: make(map[string]*v1.ConfigMap),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSecretRefValue returns the value of a secret in the supplied namespace
|
// getSecretRefValue returns the value of a secret in the supplied namespace
|
||||||
func getSecretRefValue(client clientset.Interface, namespace string, store *ResourceStore, secretSelector *api.SecretKeySelector) (string, error) {
|
func getSecretRefValue(client kubernetes.Interface, namespace string, store *ResourceStore, secretSelector *v1.SecretKeySelector) (string, error) {
|
||||||
secret, ok := store.SecretStore[secretSelector.Name]
|
secret, ok := store.SecretStore[secretSelector.Name]
|
||||||
if !ok {
|
if !ok {
|
||||||
var err error
|
var err error
|
||||||
@ -60,7 +60,7 @@ func getSecretRefValue(client clientset.Interface, namespace string, store *Reso
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getConfigMapRefValue returns the value of a configmap in the supplied namespace
|
// getConfigMapRefValue returns the value of a configmap in the supplied namespace
|
||||||
func getConfigMapRefValue(client clientset.Interface, namespace string, store *ResourceStore, configMapSelector *api.ConfigMapKeySelector) (string, error) {
|
func getConfigMapRefValue(client kubernetes.Interface, namespace string, store *ResourceStore, configMapSelector *v1.ConfigMapKeySelector) (string, error) {
|
||||||
configMap, ok := store.ConfigMapStore[configMapSelector.Name]
|
configMap, ok := store.ConfigMapStore[configMapSelector.Name]
|
||||||
if !ok {
|
if !ok {
|
||||||
var err error
|
var err error
|
||||||
@ -77,17 +77,17 @@ func getConfigMapRefValue(client clientset.Interface, namespace string, store *R
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getFieldRef returns the value of the supplied path in the given object
|
// getFieldRef returns the value of the supplied path in the given object
|
||||||
func getFieldRef(obj runtime.Object, from *api.EnvVarSource) (string, error) {
|
func getFieldRef(obj runtime.Object, from *v1.EnvVarSource) (string, error) {
|
||||||
return fieldpath.ExtractFieldPathAsString(obj, from.FieldRef.FieldPath)
|
return fieldpath.ExtractFieldPathAsString(obj, from.FieldRef.FieldPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getResourceFieldRef returns the value of a resource in the given container
|
// getResourceFieldRef returns the value of a resource in the given container
|
||||||
func getResourceFieldRef(from *api.EnvVarSource, c *api.Container) (string, error) {
|
func getResourceFieldRef(from *v1.EnvVarSource, c *v1.Container) (string, error) {
|
||||||
return resource.ExtractContainerResourceValue(from.ResourceFieldRef, c)
|
return resource.ExtractContainerResourceValue(from.ResourceFieldRef, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEnvVarRefValue returns the value referenced by the supplied EnvVarSource given the other supplied information.
|
// GetEnvVarRefValue returns the value referenced by the supplied EnvVarSource given the other supplied information.
|
||||||
func GetEnvVarRefValue(kc clientset.Interface, ns string, store *ResourceStore, from *api.EnvVarSource, obj runtime.Object, c *api.Container) (string, error) {
|
func GetEnvVarRefValue(kc kubernetes.Interface, ns string, store *ResourceStore, from *v1.EnvVarSource, obj runtime.Object, c *v1.Container) (string, error) {
|
||||||
if from.SecretKeyRef != nil {
|
if from.SecretKeyRef != nil {
|
||||||
return getSecretRefValue(kc, ns, store, from.SecretKeyRef)
|
return getSecretRefValue(kc, ns, store, from.SecretKeyRef)
|
||||||
}
|
}
|
||||||
@ -108,7 +108,7 @@ func GetEnvVarRefValue(kc clientset.Interface, ns string, store *ResourceStore,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetEnvVarRefString returns a text description of whichever field is set within the supplied EnvVarSource argument.
|
// GetEnvVarRefString returns a text description of whichever field is set within the supplied EnvVarSource argument.
|
||||||
func GetEnvVarRefString(from *api.EnvVarSource) string {
|
func GetEnvVarRefString(from *v1.EnvVarSource) string {
|
||||||
if from.ConfigMapKeyRef != nil {
|
if from.ConfigMapKeyRef != nil {
|
||||||
return fmt.Sprintf("configmap %s, key %s", from.ConfigMapKeyRef.Name, from.ConfigMapKeyRef.Key)
|
return fmt.Sprintf("configmap %s, key %s", from.ConfigMapKeyRef.Name, from.ConfigMapKeyRef.Key)
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ type ClientAccessFactory interface {
|
|||||||
|
|
||||||
// UpdatePodSpecForObject will call the provided function on the pod spec this object supports,
|
// UpdatePodSpecForObject will call the provided function on the pod spec this object supports,
|
||||||
// return false if no pod spec is supported, or return an error.
|
// return false if no pod spec is supported, or return an error.
|
||||||
UpdatePodSpecForObject(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error)
|
UpdatePodSpecForObject(obj runtime.Object, fn func(*v1.PodSpec) error) (bool, error)
|
||||||
|
|
||||||
// MapBasedSelectorForObject returns the map-based selector associated with the provided object. If a
|
// MapBasedSelectorForObject returns the map-based selector associated with the provided object. If a
|
||||||
// new set-based selector is provided, an error is returned if the selector cannot be converted to a
|
// new set-based selector is provided, an error is returned if the selector cannot be converted to a
|
||||||
|
@ -125,8 +125,13 @@ func (f *ring2Factory) PrintObject(cmd *cobra.Command, isLocal bool, mapper meta
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Prefer the existing external version if specified
|
||||||
|
var preferredVersion []string
|
||||||
|
if gvks[0].Version != "" && gvks[0].Version != runtime.APIVersionInternal {
|
||||||
|
preferredVersion = []string{gvks[0].Version}
|
||||||
|
}
|
||||||
|
|
||||||
mapping, err := mapper.RESTMapping(gvks[0].GroupKind())
|
mapping, err := mapper.RESTMapping(gvks[0].GroupKind(), preferredVersion...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -29,11 +29,17 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||||
|
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||||
|
batchv1 "k8s.io/api/batch/v1"
|
||||||
batchv2alpha1 "k8s.io/api/batch/v2alpha1"
|
batchv2alpha1 "k8s.io/api/batch/v2alpha1"
|
||||||
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@ -47,7 +53,6 @@ import (
|
|||||||
"k8s.io/client-go/util/homedir"
|
"k8s.io/client-go/util/homedir"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||||
"k8s.io/kubernetes/pkg/apis/apps"
|
"k8s.io/kubernetes/pkg/apis/apps"
|
||||||
"k8s.io/kubernetes/pkg/apis/batch"
|
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
@ -226,25 +231,49 @@ func (f *ring0Factory) JSONEncoder() runtime.Encoder {
|
|||||||
return legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...)
|
return legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *ring0Factory) UpdatePodSpecForObject(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error) {
|
func (f *ring0Factory) UpdatePodSpecForObject(obj runtime.Object, fn func(*v1.PodSpec) error) (bool, error) {
|
||||||
// TODO: replace with a swagger schema based approach (identify pod template via schema introspection)
|
// TODO: replace with a swagger schema based approach (identify pod template via schema introspection)
|
||||||
switch t := obj.(type) {
|
switch t := obj.(type) {
|
||||||
case *api.Pod:
|
case *v1.Pod:
|
||||||
return true, fn(&t.Spec)
|
return true, fn(&t.Spec)
|
||||||
case *api.ReplicationController:
|
// ReplicationController
|
||||||
|
case *v1.ReplicationController:
|
||||||
if t.Spec.Template == nil {
|
if t.Spec.Template == nil {
|
||||||
t.Spec.Template = &api.PodTemplateSpec{}
|
t.Spec.Template = &v1.PodTemplateSpec{}
|
||||||
}
|
}
|
||||||
return true, fn(&t.Spec.Template.Spec)
|
return true, fn(&t.Spec.Template.Spec)
|
||||||
case *extensions.Deployment:
|
// Deployment
|
||||||
|
case *extensionsv1beta1.Deployment:
|
||||||
return true, fn(&t.Spec.Template.Spec)
|
return true, fn(&t.Spec.Template.Spec)
|
||||||
case *extensions.DaemonSet:
|
case *appsv1beta1.Deployment:
|
||||||
return true, fn(&t.Spec.Template.Spec)
|
return true, fn(&t.Spec.Template.Spec)
|
||||||
case *extensions.ReplicaSet:
|
case *appsv1beta2.Deployment:
|
||||||
return true, fn(&t.Spec.Template.Spec)
|
return true, fn(&t.Spec.Template.Spec)
|
||||||
case *apps.StatefulSet:
|
case *appsv1.Deployment:
|
||||||
return true, fn(&t.Spec.Template.Spec)
|
return true, fn(&t.Spec.Template.Spec)
|
||||||
case *batch.Job:
|
// DaemonSet
|
||||||
|
case *extensionsv1beta1.DaemonSet:
|
||||||
|
return true, fn(&t.Spec.Template.Spec)
|
||||||
|
case *appsv1beta2.DaemonSet:
|
||||||
|
return true, fn(&t.Spec.Template.Spec)
|
||||||
|
case *appsv1.DaemonSet:
|
||||||
|
return true, fn(&t.Spec.Template.Spec)
|
||||||
|
// ReplicaSet
|
||||||
|
case *extensionsv1beta1.ReplicaSet:
|
||||||
|
return true, fn(&t.Spec.Template.Spec)
|
||||||
|
case *appsv1beta2.ReplicaSet:
|
||||||
|
return true, fn(&t.Spec.Template.Spec)
|
||||||
|
case *appsv1.ReplicaSet:
|
||||||
|
return true, fn(&t.Spec.Template.Spec)
|
||||||
|
// StatefulSet
|
||||||
|
case *appsv1beta1.StatefulSet:
|
||||||
|
return true, fn(&t.Spec.Template.Spec)
|
||||||
|
case *appsv1beta2.StatefulSet:
|
||||||
|
return true, fn(&t.Spec.Template.Spec)
|
||||||
|
case *appsv1.StatefulSet:
|
||||||
|
return true, fn(&t.Spec.Template.Spec)
|
||||||
|
// Job
|
||||||
|
case *batchv1.Job:
|
||||||
return true, fn(&t.Spec.Template.Spec)
|
return true, fn(&t.Spec.Template.Spec)
|
||||||
default:
|
default:
|
||||||
return false, fmt.Errorf("the object is not a pod or does not have a pod template")
|
return false, fmt.Errorf("the object is not a pod or does not have a pod template")
|
||||||
|
@ -31,7 +31,6 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/validation"
|
"k8s.io/apimachinery/pkg/util/validation"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeploymentV1Beta1 struct{}
|
type DeploymentV1Beta1 struct{}
|
||||||
@ -608,31 +607,6 @@ func (BasicReplicationController) ParamNames() []GeneratorParam {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// populateResourceList takes strings of form <resourceName1>=<value1>,<resourceName1>=<value2>
|
|
||||||
// and returns ResourceList.
|
|
||||||
func populateResourceList(spec string) (api.ResourceList, error) {
|
|
||||||
// empty input gets a nil response to preserve generator test expected behaviors
|
|
||||||
if spec == "" {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
result := api.ResourceList{}
|
|
||||||
resourceStatements := strings.Split(spec, ",")
|
|
||||||
for _, resourceStatement := range resourceStatements {
|
|
||||||
parts := strings.Split(resourceStatement, "=")
|
|
||||||
if len(parts) != 2 {
|
|
||||||
return nil, fmt.Errorf("Invalid argument syntax %v, expected <resource>=<value>", resourceStatement)
|
|
||||||
}
|
|
||||||
resourceName := api.ResourceName(parts[0])
|
|
||||||
resourceQuantity, err := resource.ParseQuantity(parts[1])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result[resourceName] = resourceQuantity
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// populateResourceListV1 takes strings of form <resourceName1>=<value1>,<resourceName1>=<value2>
|
// populateResourceListV1 takes strings of form <resourceName1>=<value1>,<resourceName1>=<value2>
|
||||||
// and returns ResourceList.
|
// and returns ResourceList.
|
||||||
func populateResourceListV1(spec string) (v1.ResourceList, error) {
|
func populateResourceListV1(spec string) (v1.ResourceList, error) {
|
||||||
@ -658,23 +632,6 @@ func populateResourceListV1(spec string) (v1.ResourceList, error) {
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleResourceRequirements parses the limits and requests parameters if specified
|
|
||||||
// and returns ResourceRequirements.
|
|
||||||
func HandleResourceRequirements(params map[string]string) (api.ResourceRequirements, error) {
|
|
||||||
result := api.ResourceRequirements{}
|
|
||||||
limits, err := populateResourceList(params["limits"])
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
result.Limits = limits
|
|
||||||
requests, err := populateResourceList(params["requests"])
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
result.Requests = requests
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleResourceRequirementsV1 parses the limits and requests parameters if specified
|
// HandleResourceRequirementsV1 parses the limits and requests parameters if specified
|
||||||
// and returns ResourceRequirements.
|
// and returns ResourceRequirements.
|
||||||
func HandleResourceRequirementsV1(params map[string]string) (v1.ResourceRequirements, error) {
|
func HandleResourceRequirementsV1(params map[string]string) (v1.ResourceRequirements, error) {
|
||||||
|
Loading…
Reference in New Issue
Block a user