remove todo: switch UpdatePodSpecForObject to work on v1.PodSpec, use info.VersionedObject, and avoid conversion completely

This commit is contained in:
zhengjiajin 2017-11-11 02:36:12 +08:00
parent 4793b714d8
commit dc312142ab
21 changed files with 1525 additions and 228 deletions

View File

@ -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",

View File

@ -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) {

View File

@ -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,9 +215,13 @@ 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 err != nil { if o.List {
return err client, err := f.KubernetesClientSet()
if err != nil {
return err
}
kubeClient = client
} }
cmdNamespace, enforceNamespace, err := f.DefaultNamespace() cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
@ -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

View File

@ -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)
}
}

View File

@ -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

View File

@ -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)
}
}

View File

@ -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")
} }

View File

@ -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)
}
}

View File

@ -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")
} }

View File

@ -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
args []string testAPIGroup 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}

View File

@ -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",

View File

@ -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
} }

View File

@ -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",

View File

@ -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",
], ],
) )

View File

@ -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],
}) })

View File

@ -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() {

View File

@ -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)
} }

View File

@ -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

View File

@ -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
} }

View File

@ -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")

View File

@ -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) {