Merge pull request #63318 from deads2k/cli-40-mapperfunc

Automatic merge from submit-queue (batch tested with PRs 63315, 63383, 63318, 63439). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

drive resourcebuilder from kubeconfig, allow scheme specification

This updates the resource builder to work based on a kubeconfig to allow future chaining of flags.  It also allow specification of the scheme you want for your actual decoding.


@kubernetes/sig-cli-maintainers 

```release-note
NONE
```
This commit is contained in:
Kubernetes Submit Queue
2018-05-04 12:41:12 -07:00
committed by GitHub
50 changed files with 548 additions and 386 deletions

View File

@@ -102,6 +102,7 @@ go_library(
"//vendor/github.com/renstrom/dedent:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library",
"//vendor/k8s.io/api/autoscaling/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/policy/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
@@ -129,7 +130,9 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
"//vendor/k8s.io/client-go/discovery:go_default_library",
"//vendor/k8s.io/client-go/dynamic:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v1:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/scale:go_default_library",

View File

@@ -38,6 +38,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/dynamic"
scaleclient "k8s.io/client-go/scale"
oapi "k8s.io/kube-openapi/pkg/util/proto"
api "k8s.io/kubernetes/pkg/apis/core"
@@ -285,8 +286,13 @@ func (o *ApplyOptions) Run(f cmdutil.Factory, cmd *cobra.Command) error {
return err
}
mapper, err := f.RESTMapper()
if err != nil {
return err
}
if o.Prune {
o.PruneResources, err = parsePruneResources(r.Mapper().RESTMapper, o.PruneWhitelist)
o.PruneResources, err = parsePruneResources(mapper, o.PruneWhitelist)
if err != nil {
return err
}
@@ -297,7 +303,6 @@ func (o *ApplyOptions) Run(f cmdutil.Factory, cmd *cobra.Command) error {
encoder := scheme.DefaultJSONEncoder()
deserializer := scheme.Codecs.UniversalDeserializer()
mapper := r.Mapper().RESTMapper
visitedUids := sets.NewString()
visitedNamespaces := sets.NewString()
@@ -382,12 +387,16 @@ func (o *ApplyOptions) Run(f cmdutil.Factory, cmd *cobra.Command) error {
return err
}
helper := resource.NewHelper(info.Client, info.Mapping)
dynamicClient, err := f.DynamicClient()
if err != nil {
return err
}
patcher := &patcher{
encoder: encoder,
decoder: deserializer,
mapping: info.Mapping,
helper: helper,
clientFunc: f.UnstructuredClientForMapping,
dynamicClient: dynamicClient,
clientsetFunc: f.ClientSet,
overwrite: o.Overwrite,
backOff: clockwork.NewRealClock(),
@@ -470,9 +479,14 @@ func (o *ApplyOptions) Run(f cmdutil.Factory, cmd *cobra.Command) error {
return nil
}
dynamicClient, err := f.DynamicClient()
if err != nil {
return err
}
p := pruner{
mapper: mapper,
clientFunc: f.UnstructuredClientForMapping,
dynamicClient: dynamicClient,
clientsetFunc: f.ClientSet,
labelSelector: o.Selector,
@@ -560,7 +574,7 @@ func getRESTMappings(mapper meta.RESTMapper, pruneResources *[]pruneResource) (n
type pruner struct {
mapper meta.RESTMapper
clientFunc resource.ClientMapperFunc
dynamicClient dynamic.DynamicInterface
clientsetFunc func() (internalclientset.Interface, error)
visitedUids sets.String
@@ -577,24 +591,17 @@ type pruner struct {
}
func (p *pruner) prune(f cmdutil.Factory, namespace string, mapping *meta.RESTMapping, includeUninitialized bool) error {
c, err := p.clientFunc(mapping)
objList, err := p.dynamicClient.Resource(mapping.Resource).
Namespace(namespace).
List(metav1.ListOptions{
LabelSelector: p.labelSelector,
FieldSelector: p.fieldSelector,
IncludeUninitialized: includeUninitialized,
})
if err != nil {
return err
}
objList, err := resource.NewHelper(c, mapping).List(
namespace,
mapping.GroupVersionKind.Version,
false,
&metav1.ListOptions{
LabelSelector: p.labelSelector,
FieldSelector: p.fieldSelector,
IncludeUninitialized: includeUninitialized,
},
)
if err != nil {
return err
}
objs, err := meta.ExtractList(objList)
if err != nil {
return err
@@ -635,20 +642,12 @@ func (p *pruner) prune(f cmdutil.Factory, namespace string, mapping *meta.RESTMa
}
func (p *pruner) delete(namespace, name string, mapping *meta.RESTMapping, scaleClient scaleclient.ScalesGetter) error {
c, err := p.clientFunc(mapping)
if err != nil {
return err
}
return runDelete(namespace, name, mapping, c, nil, p.cascade, p.gracePeriod, p.clientsetFunc, scaleClient)
return runDelete(namespace, name, mapping, p.dynamicClient, p.cascade, p.gracePeriod, p.clientsetFunc, scaleClient)
}
func runDelete(namespace, name string, mapping *meta.RESTMapping, c resource.RESTClient, helper *resource.Helper, cascade bool, gracePeriod int, clientsetFunc func() (internalclientset.Interface, error), scaleClient scaleclient.ScalesGetter) error {
func runDelete(namespace, name string, mapping *meta.RESTMapping, c dynamic.DynamicInterface, cascade bool, gracePeriod int, clientsetFunc func() (internalclientset.Interface, error), scaleClient scaleclient.ScalesGetter) error {
if !cascade {
if helper == nil {
helper = resource.NewHelper(c, mapping)
}
return helper.Delete(namespace, name)
return c.Resource(mapping.Resource).Namespace(namespace).Delete(name, nil)
}
cs, err := clientsetFunc()
if err != nil {
@@ -659,7 +658,7 @@ func runDelete(namespace, name string, mapping *meta.RESTMapping, c resource.RES
if _, ok := err.(*kubectl.NoSuchReaperError); !ok {
return err
}
return resource.NewHelper(c, mapping).Delete(namespace, name)
return c.Resource(mapping.Resource).Namespace(namespace).Delete(name, nil)
}
var options *metav1.DeleteOptions
if gracePeriod >= 0 {
@@ -672,11 +671,7 @@ func runDelete(namespace, name string, mapping *meta.RESTMapping, c resource.RES
}
func (p *patcher) delete(namespace, name string) error {
c, err := p.clientFunc(p.mapping)
if err != nil {
return err
}
return runDelete(namespace, name, p.mapping, c, p.helper, p.cascade, p.gracePeriod, p.clientsetFunc, p.scaleClient)
return runDelete(namespace, name, p.mapping, p.dynamicClient, p.cascade, p.gracePeriod, p.clientsetFunc, p.scaleClient)
}
type patcher struct {
@@ -685,7 +680,7 @@ type patcher struct {
mapping *meta.RESTMapping
helper *resource.Helper
clientFunc resource.ClientMapperFunc
dynamicClient dynamic.DynamicInterface
clientsetFunc func() (internalclientset.Interface, error)
overwrite bool

View File

@@ -146,7 +146,7 @@ func (p *AttachOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn [
}
builder := f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
NamespaceParam(namespace).DefaultNamespace()
switch len(argsIn) {

View File

@@ -96,7 +96,7 @@ func (o *ReconcileOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args
}
r := f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
ContinueOnError().
NamespaceParam(namespace).DefaultNamespace().
FilenameParam(enforceNamespace, options).

View File

@@ -22,8 +22,10 @@ import (
"github.com/golang/glog"
"github.com/spf13/cobra"
autoscalingv1 "k8s.io/api/autoscaling/v1"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema"
autoscalingv1client "k8s.io/client-go/kubernetes/typed/autoscaling/v1"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
@@ -70,11 +72,11 @@ type AutoscaleOptions struct {
namespace string
dryRun bool
builder *resource.Builder
mapper meta.RESTMapper
canBeAutoscaled func(kind schema.GroupKind) error
clientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
generatorFunc func(string, *meta.RESTMapping) (kubectl.StructuredGenerator, error)
HPAClient autoscalingv1client.HorizontalPodAutoscalersGetter
genericclioptions.IOStreams
}
@@ -132,12 +134,6 @@ func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.builder = f.NewBuilder()
o.canBeAutoscaled = f.CanBeAutoscaled
o.mapper, err = f.RESTMapper()
if err != nil {
return err
}
o.clientForMapping = f.ClientForMapping
o.args = args
o.RecordFlags.Complete(f.Command(cmd, false))
@@ -146,6 +142,12 @@ func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
return err
}
kubeClient, err := f.KubernetesClientSet()
if err != nil {
return err
}
o.HPAClient = kubeClient.AutoscalingV1()
// get the generator
o.generatorFunc = func(name string, mapping *meta.RESTMapping) (kubectl.StructuredGenerator, error) {
switch o.Generator {
@@ -199,7 +201,7 @@ func (o *AutoscaleOptions) Validate() error {
func (o *AutoscaleOptions) Run() error {
r := o.builder.
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
ContinueOnError().
NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(o.enforceNamespace, o.FilenameOptions).
@@ -231,20 +233,14 @@ func (o *AutoscaleOptions) Run() error {
if err != nil {
return err
}
hpa, ok := object.(*autoscalingv1.HorizontalPodAutoscaler)
if !ok {
return fmt.Errorf("generator made %T, not autoscalingv1.HorizontalPodAutoscaler", object)
}
resourceMapper := &resource.Mapper{
RESTMapper: o.mapper,
ClientMapper: resource.ClientMapperFunc(o.clientForMapping),
Decoder: cmdutil.InternalVersionDecoder(),
}
hpa, err := resourceMapper.InfoForObject(object, legacyscheme.Scheme, nil)
if err != nil {
return err
}
if err := o.Recorder.Record(hpa.Object); err != nil {
if err := o.Recorder.Record(hpa); err != nil {
glog.V(4).Infof("error recording current command: %v", err)
}
object = hpa.Object
if o.dryRun {
count++
@@ -253,14 +249,14 @@ func (o *AutoscaleOptions) Run() error {
if err != nil {
return err
}
return printer.PrintObj(cmdutil.AsDefaultVersionedOrOriginal(hpa.Object, hpa.Mapping), o.Out)
return printer.PrintObj(hpa, o.Out)
}
if err := kubectl.CreateOrUpdateAnnotation(o.createAnnotation, hpa.Object, cmdutil.InternalVersionJSONEncoder()); err != nil {
if err := kubectl.CreateOrUpdateAnnotation(o.createAnnotation, hpa, cmdutil.InternalVersionJSONEncoder()); err != nil {
return err
}
_, err = resource.NewHelper(hpa.Client, hpa.Mapping).Create(o.namespace, false, object)
actualHPA, err := o.HPAClient.HorizontalPodAutoscalers(o.namespace).Create(hpa)
if err != nil {
return err
}
@@ -270,7 +266,7 @@ func (o *AutoscaleOptions) Run() error {
if err != nil {
return err
}
return printer.PrintObj(cmdutil.AsDefaultVersionedOrOriginal(info.Object, hpa.Mapping), o.Out)
return printer.PrintObj(actualHPA, o.Out)
})
if err != nil {
return err

View File

@@ -204,7 +204,7 @@ func (o *CertificateOptions) RunCertificateDeny(force bool) error {
func (options *CertificateOptions) modifyCertificateCondition(builder *resource.Builder, clientSet internalclientset.Interface, force bool, modify func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, bool)) error {
var found int
r := builder.
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
ContinueOnError().
FilenameParam(false, &options.FilenameOptions).
ResourceNames("certificatesigningrequest", options.csrNames...).

View File

@@ -96,7 +96,7 @@ func (o *ClusterInfoOptions) Run() error {
// TODO use generalized labels once they are implemented (#341)
b := o.Builder.
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
NamespaceParam(o.Namespace).DefaultNamespace().
LabelSelectorParam("kubernetes.io/cluster-service=true").
ResourceTypeOrNameArgs(false, []string{"services"}...).

View File

@@ -129,7 +129,7 @@ func (o *ConvertOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) (err er
// build the builder
o.builder = f.NewBuilder().
Internal(scheme.Scheme).
WithScheme(scheme.Scheme).
LocalParam(o.local)
if !o.local {
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))

View File

@@ -41,6 +41,7 @@ go_library(
"//vendor/k8s.io/api/rbac/v1: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/unstructured: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/util/sets:go_default_library",

View File

@@ -29,6 +29,7 @@ import (
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
kruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/api/legacyscheme"
@@ -404,29 +405,29 @@ func RunCreateSubcommand(f cmdutil.Factory, options *CreateSubcommandOptions) er
if err != nil {
return err
}
client, err := f.ClientForMapping(mapping)
if err != nil {
return err
}
resourceMapper := &resource.Mapper{
RESTMapper: mapper,
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
}
info, err := resourceMapper.InfoForObject(obj, legacyscheme.Scheme, nil)
if err != nil {
return err
}
if err := kubectl.CreateOrUpdateAnnotation(options.CreateAnnotation, info.Object, cmdutil.InternalVersionJSONEncoder()); err != nil {
if err := kubectl.CreateOrUpdateAnnotation(options.CreateAnnotation, obj, cmdutil.InternalVersionJSONEncoder()); err != nil {
return err
}
obj, err = resource.NewHelper(client, mapping).Create(namespace, false, info.Object)
asUnstructured := &unstructured.Unstructured{}
if err := legacyscheme.Scheme.Convert(obj, asUnstructured, nil); err != nil {
return err
}
dynamicClient, err := f.DynamicClient()
if err != nil {
return err
}
if mapping.Scope.Name() == meta.RESTScopeNameRoot {
namespace = ""
}
actualObject, err := dynamicClient.Resource(mapping.Resource).Namespace(namespace).Create(asUnstructured)
if err != nil {
return err
}
// ensure we pass a versioned object to the printer
obj = cmdutil.AsDefaultVersionedOrOriginal(info.Object, info.Mapping)
obj = actualObject
} else {
if meta, err := meta.Accessor(obj); err == nil && nsOverriden {
meta.SetNamespace(namespace)

View File

@@ -17,41 +17,16 @@ limitations under the License.
package create
import (
"net/http"
"testing"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
func TestCreateQuota(t *testing.T) {
resourceQuotaObject := &v1.ResourceQuota{}
resourceQuotaObject.Name = "my-quota"
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; {
case p == "/namespaces/test/resourcequotas" && m == "POST":
return &http.Response{StatusCode: 201, Header: defaultHeader(), Body: objBody(codec, resourceQuotaObject)}, nil
default:
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil
}
}),
}
tf.Namespace = "test"
tests := map[string]struct {
flags []string
@@ -75,6 +50,12 @@ func TestCreateQuota(t *testing.T) {
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
tf.Namespace = "test"
ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdCreateQuota(tf, ioStreams)
cmd.Flags().Parse(test.flags)
@@ -84,5 +65,6 @@ func TestCreateQuota(t *testing.T) {
if buf.String() != test.expectedOutput {
t.Errorf("%s: expected output: %s, but got: %s", name, test.expectedOutput, buf.String())
}
})
}
}

View File

@@ -171,7 +171,11 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args
return err
}
o.Result = r
o.Mapper = r.Mapper().RESTMapper
o.Mapper, err = f.RESTMapper()
if err != nil {
return err
}
// Set up writer
o.Out = out

View File

@@ -282,7 +282,7 @@ func (o *DrainOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st
}
builder := f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
NamespaceParam(o.Namespace).DefaultNamespace().
ResourceNames("nodes", args...).
SingleResourceType().

View File

@@ -32,7 +32,7 @@ import (
yaml "gopkg.in/yaml.v2"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/rest/fake"
@@ -211,12 +211,12 @@ func TestEdit(t *testing.T) {
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
tf.UnstructuredClientForMappingFunc = func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
tf.UnstructuredClientForMappingFunc = func(gv schema.GroupVersion) (resource.RESTClient, error) {
versionedAPIPath := ""
if mapping.GroupVersionKind.Group == "" {
versionedAPIPath = "/api/" + mapping.GroupVersionKind.Version
if gv.Group == "" {
versionedAPIPath = "/api/" + gv.Version
} else {
versionedAPIPath = "/apis/" + mapping.GroupVersionKind.Group + "/" + mapping.GroupVersionKind.Version
versionedAPIPath = "/apis/" + gv.Group + "/" + gv.Version
}
return &fake.RESTClient{
VersionedAPIPath: versionedAPIPath,

View File

@@ -24,9 +24,12 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructuredscheme"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/client-go/dynamic"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
@@ -97,6 +100,7 @@ type ExposeServiceOptions struct {
Namespace string
Mapper meta.RESTMapper
DynamicClient dynamic.DynamicInterface
Builder *resource.Builder
Recorder genericclioptions.Recorder
@@ -180,6 +184,11 @@ func (o *ExposeServiceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) e
return err
}
o.DynamicClient, err = f.DynamicClient()
if err != nil {
return err
}
o.Generators = f.Generators
o.Builder = f.NewBuilder()
o.CanBeExposed = f.CanBeExposed
@@ -203,7 +212,7 @@ func (o *ExposeServiceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) e
func (o *ExposeServiceOptions) RunExpose(cmd *cobra.Command, args []string) error {
r := o.Builder.
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
ContinueOnError().
NamespaceParam(o.Namespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, &o.FilenameOptions).
@@ -313,33 +322,36 @@ func (o *ExposeServiceOptions) RunExpose(cmd *cobra.Command, args []string) erro
}
}
resourceMapper := &resource.Mapper{
RESTMapper: o.Mapper,
ClientMapper: resource.ClientMapperFunc(o.ClientForMapping),
Decoder: cmdutil.InternalVersionDecoder(),
}
info, err = resourceMapper.InfoForObject(object, legacyscheme.Scheme, nil)
if err != nil {
return err
}
if err := o.Recorder.Record(object); err != nil {
glog.V(4).Infof("error recording current command: %v", err)
}
info.Refresh(object, true)
if o.DryRun {
return o.PrintObj(object, o.Out)
}
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info.Object, cmdutil.InternalVersionJSONEncoder()); err != nil {
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), object, cmdutil.InternalVersionJSONEncoder()); err != nil {
return err
}
asUnstructured := &unstructured.Unstructured{}
if err := legacyscheme.Scheme.Convert(object, asUnstructured, nil); err != nil {
return err
}
gvks, _, err := unstructuredscheme.NewUnstructuredObjectTyper().ObjectKinds(asUnstructured)
if err != nil {
return err
}
objMapping, err := o.Mapper.RESTMapping(gvks[0].GroupKind(), gvks[0].Version)
if err != nil {
return err
}
// Serialize the object with the annotation applied.
object, err = resource.NewHelper(info.Client, info.Mapping).Create(o.Namespace, false, object)
actualObject, err := o.DynamicClient.Resource(objMapping.Resource).Namespace(o.Namespace).Create(asUnstructured)
if err != nil {
return err
}
return o.PrintObj(cmdutil.AsDefaultVersionedOrOriginal(info.Object, info.Mapping), o.Out)
return o.PrintObj(actualObject, o.Out)
})
if err != nil {
return err

View File

@@ -480,8 +480,6 @@ func TestRunExposeService(t *testing.T) {
switch p, m := req.URL.Path, req.Method; {
case p == test.calls[m] && m == "GET":
return &http.Response{StatusCode: test.status, Header: defaultHeader(), Body: objBody(codec, test.input)}, nil
case p == test.calls[m] && m == "POST":
return &http.Response{StatusCode: test.status, Header: defaultHeader(), Body: objBody(codec, test.output)}, nil
default:
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil

View File

@@ -202,7 +202,7 @@ func (o *LogsOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Comm
if o.Object == nil {
builder := f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
NamespaceParam(o.Namespace).DefaultNamespace().
SingleResourceType()
if o.ResourceArg != "" {

View File

@@ -180,7 +180,7 @@ func (o *PortForwardOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, arg
}
builder := f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
ContinueOnError().
NamespaceParam(o.Namespace).DefaultNamespace()

View File

@@ -82,7 +82,7 @@ func RunHistory(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []str
}
r := f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options).
ResourceTypeOrNameArgs(true, args...).

View File

@@ -111,7 +111,7 @@ func (o *PauseConfig) CompletePause(f cmdutil.Factory, cmd *cobra.Command, args
}
r := f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(true, args...).

View File

@@ -119,7 +119,7 @@ func (o *ResumeConfig) CompleteResume(f cmdutil.Factory, cmd *cobra.Command, arg
}
r := f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(true, args...).

View File

@@ -124,7 +124,7 @@ func (o *RolloutStatusOptions) Validate(cmd *cobra.Command, args []string) error
func (o *RolloutStatusOptions) Run() error {
r := o.Builder.
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
NamespaceParam(o.Namespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, o.FilenameOptions).
ResourceTypeOrNameArgs(true, o.BuilderArgs...).

View File

@@ -127,7 +127,7 @@ func (o *UndoOptions) CompleteUndo(f cmdutil.Factory, cmd *cobra.Command, out io
}
r := f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(true, args...).

View File

@@ -20,6 +20,7 @@ import (
"fmt"
"io"
"k8s.io/client-go/dynamic"
"k8s.io/kubernetes/pkg/printers"
"github.com/docker/distribution/reference"
@@ -40,6 +41,7 @@ import (
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/util/interrupt"
uexec "k8s.io/utils/exec"
@@ -90,9 +92,7 @@ var (
)
type RunObject struct {
Versioned runtime.Object
Object runtime.Object
Kind string
Mapping *meta.RESTMapping
}
@@ -107,6 +107,8 @@ type RunOptions struct {
PrintObj func(runtime.Object) error
Recorder genericclioptions.Recorder
DynamicClient dynamic.DynamicInterface
ArgsLenAtDash int
Attach bool
Expose bool
@@ -197,6 +199,11 @@ func (o *RunOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
return err
}
o.DynamicClient, err = f.DynamicClient()
if err != nil {
return err
}
o.ArgsLenAtDash = cmd.ArgsLenAtDash()
o.DryRun = cmdutil.GetFlagBool(cmd, "dry-run")
o.Expose = cmdutil.GetFlagBool(cmd, "expose")
@@ -438,7 +445,7 @@ func (o *RunOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
}
if runObject != nil {
if err := o.PrintObj(runObject.Versioned); err != nil {
if err := o.PrintObj(runObject.Object); err != nil {
return err
}
}
@@ -458,7 +465,7 @@ func (o *RunOptions) removeCreatedObjects(f cmdutil.Factory, createdObjects []*R
return err
}
r := f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
ContinueOnError().
NamespaceParam(namespace).DefaultNamespace().
ResourceNames(obj.Mapping.Resource.Resource+"."+obj.Mapping.Resource.Group, name).
@@ -620,7 +627,7 @@ func (o *RunOptions) generateService(f cmdutil.Factory, cmd *cobra.Command, serv
return nil, err
}
if err := o.PrintObj(runObject.Versioned); err != nil {
if err := o.PrintObj(runObject.Object); err != nil {
return nil, err
}
// separate yaml objects
@@ -648,60 +655,45 @@ func (o *RunOptions) createGeneratedObject(f cmdutil.Factory, cmd *cobra.Command
return nil, err
}
// run has compiled knowledge of the thing is is creating
groupVersionKinds, _, err := legacyscheme.Scheme.ObjectKinds(obj)
gvks, _, err := scheme.Scheme.ObjectKinds(obj)
if err != nil {
return nil, err
}
mapping, err := mapper.RESTMapping(gvks[0].GroupKind(), gvks[0].Version)
if err != nil {
return nil, err
}
groupVersionKind := groupVersionKinds[0]
if len(overrides) > 0 {
codec := runtime.NewCodec(cmdutil.InternalVersionJSONEncoder(), cmdutil.InternalVersionDecoder())
codec := runtime.NewCodec(scheme.DefaultJSONEncoder(), scheme.Codecs.UniversalDecoder(scheme.Registry.RegisteredGroupVersions()...))
obj, err = cmdutil.Merge(codec, obj, overrides)
if err != nil {
return nil, err
}
}
mapping, err := mapper.RESTMapping(groupVersionKind.GroupKind(), groupVersionKind.Version)
if err != nil {
if err := o.Recorder.Record(obj); err != nil {
glog.V(4).Infof("error recording current command: %v", err)
}
actualObj := obj
if !o.DryRun {
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), obj, scheme.DefaultJSONEncoder()); err != nil {
return nil, err
}
client, err := f.ClientForMapping(mapping)
if err != nil {
return nil, err
}
if err := o.Recorder.Record(obj); err != nil {
glog.V(4).Infof("error recording current command: %v", err)
}
versioned := obj
if !o.DryRun {
resourceMapper := &resource.Mapper{
RESTMapper: mapper,
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
Decoder: cmdutil.InternalVersionDecoder(),
}
info, err := resourceMapper.InfoForObject(obj, legacyscheme.Scheme, nil)
actualObj, err = resource.NewHelper(client, mapping).Create(namespace, false, obj)
if err != nil {
return nil, err
}
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info.Object, cmdutil.InternalVersionJSONEncoder()); err != nil {
return nil, err
}
actualObj = cmdutil.AsDefaultVersionedOrOriginal(actualObj, mapping)
obj, err = resource.NewHelper(client, mapping).Create(namespace, false, info.Object)
if err != nil {
return nil, err
}
versioned = cmdutil.AsDefaultVersionedOrOriginal(info.Object, info.Mapping)
}
return &RunObject{
Versioned: versioned,
Object: obj,
Kind: groupVersionKind.Kind,
Object: actualObj,
Mapping: mapping,
}, nil
}

View File

@@ -27,6 +27,7 @@ go_library(
"//pkg/kubectl/cmd/util/env:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/kubectl/scheme:go_default_library",
"//pkg/kubectl/util/i18n:go_default_library",
"//pkg/printers:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
@@ -82,6 +83,7 @@ go_test(
"//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/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/rest/fake:go_default_library",
],

View File

@@ -30,12 +30,12 @@ import (
"k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
envutil "k8s.io/kubernetes/pkg/kubectl/cmd/util/env"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/printers"
)
@@ -241,7 +241,7 @@ func (o *EnvOptions) RunEnv() error {
if len(o.From) != 0 {
b := o.builder().
Internal(legacyscheme.Scheme).
WithScheme(scheme.Scheme, scheme.Registry.RegisteredGroupVersions()...).
LocalParam(o.Local).
ContinueOnError().
NamespaceParam(o.namespace).DefaultNamespace().
@@ -261,11 +261,7 @@ func (o *EnvOptions) RunEnv() error {
}
for _, info := range infos {
versionedObject, err := legacyscheme.Scheme.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion())
if err != nil {
return err
}
switch from := versionedObject.(type) {
switch from := info.Object.(type) {
case *v1.Secret:
for key := range from.Data {
envVar := v1.EnvVar{
@@ -309,7 +305,7 @@ func (o *EnvOptions) RunEnv() error {
}
b := o.builder().
Internal(legacyscheme.Scheme).
WithScheme(scheme.Scheme, scheme.Registry.RegisteredGroupVersions()...).
LocalParam(o.Local).
ContinueOnError().
NamespaceParam(o.namespace).DefaultNamespace().
@@ -326,8 +322,7 @@ func (o *EnvOptions) RunEnv() error {
if err != nil {
return err
}
patches := CalculatePatches(infos, cmdutil.InternalVersionJSONEncoder(), func(info *resource.Info) ([]byte, error) {
info.Object = cmdutil.AsDefaultVersionedOrOriginal(info.Object, info.Mapping)
patches := CalculatePatches(infos, scheme.DefaultJSONEncoder(), func(info *resource.Info) ([]byte, error) {
_, err := o.updatePodSpecForObject(info.Object, func(spec *v1.PodSpec) error {
resolutionErrorsEncountered := false
containers, _ := selectContainers(spec.Containers, o.ContainerSelector)
@@ -394,7 +389,7 @@ func (o *EnvOptions) RunEnv() error {
})
if err == nil {
return runtime.Encode(cmdutil.InternalVersionJSONEncoder(), info.Object)
return runtime.Encode(scheme.DefaultJSONEncoder(), info.Object)
}
return nil, err
})
@@ -418,7 +413,7 @@ func (o *EnvOptions) RunEnv() error {
}
if o.Local || o.dryRun {
if err := o.PrintObj(cmdutil.AsDefaultVersionedOrOriginal(patch.Info.Object, patch.Info.Mapping), o.Out); err != nil {
if err := o.PrintObj(patch.Info.Object, o.Out); err != nil {
return err
}
continue
@@ -437,7 +432,7 @@ func (o *EnvOptions) RunEnv() error {
return fmt.Errorf("at least one environment variable must be provided")
}
if err := o.PrintObj(cmdutil.AsDefaultVersionedOrOriginal(info.Object, info.Mapping), o.Out); err != nil {
if err := o.PrintObj(info.Object, o.Out); err != nil {
return err
}
}

View File

@@ -36,9 +36,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/testapi"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
@@ -50,7 +50,7 @@ func TestSetEnvLocal(t *testing.T) {
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
ns := legacyscheme.Codecs
ns := serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: ""},
NegotiatedSerializer: ns,
@@ -93,7 +93,7 @@ func TestSetMultiResourcesEnvLocal(t *testing.T) {
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
ns := legacyscheme.Codecs
ns := serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: ""},
NegotiatedSerializer: ns,
@@ -464,7 +464,7 @@ func TestSetEnvRemote(t *testing.T) {
defer tf.Cleanup()
codec := scheme.Codecs.CodecForVersions(scheme.Codecs.LegacyCodec(groupVersion), scheme.Codecs.UniversalDecoder(groupVersion), groupVersion, groupVersion)
ns := legacyscheme.Codecs
ns := serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
tf.Namespace = "test"
tf.Client = &fake.RESTClient{
GroupVersion: groupVersion,

View File

@@ -19,11 +19,9 @@ package set
import (
"fmt"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/printers"
"github.com/golang/glog"
"github.com/spf13/cobra"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
@@ -32,7 +30,9 @@ import (
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
)
// ImageOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of
@@ -161,7 +161,7 @@ func (o *SetImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
builder := f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(scheme.Scheme, scheme.Registry.RegisteredGroupVersions()...).
LocalParam(o.Local).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
@@ -209,9 +209,8 @@ func (o *SetImageOptions) Validate() error {
func (o *SetImageOptions) Run() error {
allErrs := []error{}
patches := CalculatePatches(o.Infos, cmdutil.InternalVersionJSONEncoder(), func(info *resource.Info) ([]byte, error) {
patches := CalculatePatches(o.Infos, scheme.DefaultJSONEncoder(), func(info *resource.Info) ([]byte, error) {
transformed := false
info.Object = cmdutil.AsDefaultVersionedOrOriginal(info.Object, info.Mapping)
_, err := o.UpdatePodSpecForObject(info.Object, func(spec *v1.PodSpec) error {
for name, image := range o.ContainerImages {
var (
@@ -259,7 +258,7 @@ func (o *SetImageOptions) Run() error {
glog.V(4).Infof("error recording current command: %v", err)
}
return runtime.Encode(cmdutil.InternalVersionJSONEncoder(), info.Object)
return runtime.Encode(scheme.DefaultJSONEncoder(), info.Object)
})
for _, patch := range patches {
@@ -275,7 +274,7 @@ func (o *SetImageOptions) Run() error {
}
if o.Local || o.DryRun {
if err := o.PrintObj(cmdutil.AsDefaultVersionedOrOriginal(patch.Info.Object, patch.Info.Mapping), o.Out); err != nil {
if err := o.PrintObj(patch.Info.Object, o.Out); err != nil {
return err
}
continue
@@ -289,7 +288,7 @@ func (o *SetImageOptions) Run() error {
}
info.Refresh(obj, true)
if err := o.PrintObj(cmdutil.AsDefaultVersionedOrOriginal(info.Object, info.Mapping), o.Out); err != nil {
if err := o.PrintObj(info.Object, o.Out); err != nil {
return err
}
}

View File

@@ -24,9 +24,8 @@ import (
"strings"
"testing"
"k8s.io/kubernetes/pkg/printers"
"github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
@@ -36,21 +35,22 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/testapi"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/printers"
)
func TestImageLocal(t *testing.T) {
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
ns := legacyscheme.Codecs
ns := serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: ""},
@@ -173,7 +173,7 @@ func TestSetMultiResourcesImageLocal(t *testing.T) {
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
ns := legacyscheme.Codecs
ns := serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: ""},
@@ -554,7 +554,7 @@ func TestSetImageRemote(t *testing.T) {
defer tf.Cleanup()
codec := scheme.Codecs.CodecForVersions(scheme.Codecs.LegacyCodec(groupVersion), scheme.Codecs.UniversalDecoder(groupVersion), groupVersion, groupVersion)
ns := legacyscheme.Codecs
ns := serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
tf.Namespace = "test"
tf.Client = &fake.RESTClient{
GroupVersion: groupVersion,

View File

@@ -173,7 +173,7 @@ func (o *SetResourcesOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, ar
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
builder := f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
LocalParam(o.Local).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().

View File

@@ -146,7 +146,7 @@ func (o *SetSelectorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, arg
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
o.builder = f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
LocalParam(o.local).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().

View File

@@ -152,7 +152,7 @@ func (o *SetServiceAccountOptions) Complete(f cmdutil.Factory, cmd *cobra.Comman
resources := args[:len(args)-1]
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
builder := f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
LocalParam(o.local).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().

View File

@@ -138,7 +138,7 @@ func (o *SubjectOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
builder := f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
LocalParam(o.Local).
ContinueOnError().
NamespaceParam(o.namespace).DefaultNamespace().

View File

@@ -172,7 +172,7 @@ func (o *TaintOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st
return cmdutil.UsageErrorf(cmd, err.Error())
}
o.builder = f.NewBuilder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
ContinueOnError().
NamespaceParam(namespace).DefaultNamespace()
if o.selector != "" {

View File

@@ -24,11 +24,12 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta/testrestmapper: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/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//vendor/k8s.io/client-go/discovery:go_default_library",
"//vendor/k8s.io/client-go/dynamic:go_default_library",
"//vendor/k8s.io/client-go/dynamic/fake:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/rest/fake:go_default_library",

View File

@@ -30,11 +30,12 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/api/meta/testrestmapper"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
fakedynamic "k8s.io/client-go/dynamic/fake"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
@@ -241,10 +242,11 @@ type TestFactory struct {
Namespace string
ClientConfigVal *restclient.Config
CommandVal string
FakeDynamicClient *fakedynamic.FakeDynamicClient
tempConfigFile *os.File
UnstructuredClientForMappingFunc func(mapping *meta.RESTMapping) (resource.RESTClient, error)
UnstructuredClientForMappingFunc resource.FakeClientFunc
OpenAPISchemaFunc func() (openapi.Resources, error)
}
@@ -254,6 +256,7 @@ func NewTestFactory() *TestFactory {
config, configFile := defaultFakeClientConfig()
return &TestFactory{
Factory: cmdutil.NewFactory(config),
FakeDynamicClient: fakedynamic.NewSimpleDynamicClient(legacyscheme.Scheme),
tempConfigFile: configFile,
}
}
@@ -309,7 +312,7 @@ func (f *TestFactory) ClientForMapping(mapping *meta.RESTMapping) (resource.REST
func (f *TestFactory) UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
if f.UnstructuredClientForMappingFunc != nil {
return f.UnstructuredClientForMappingFunc(mapping)
return f.UnstructuredClientForMappingFunc(mapping.GroupVersionKind.GroupVersion())
}
return f.UnstructuredClient, nil
}
@@ -340,17 +343,17 @@ func (f *TestFactory) Command(*cobra.Command, bool) string {
func (f *TestFactory) NewBuilder() *resource.Builder {
mapper, err := f.RESTMapper()
return resource.NewBuilder(
&resource.Mapper{
RESTMapper: mapper,
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
Decoder: cmdutil.InternalVersionDecoder(),
},
&resource.Mapper{
RESTMapper: mapper,
ClientMapper: resource.ClientMapperFunc(f.UnstructuredClientForMapping),
Decoder: unstructured.UnstructuredJSONScheme,
return resource.NewFakeBuilder(
func(version schema.GroupVersion) (resource.RESTClient, error) {
if f.UnstructuredClientForMappingFunc != nil {
return f.UnstructuredClientForMappingFunc(version)
}
if f.UnstructuredClient != nil {
return f.UnstructuredClient, nil
}
return f.Client, nil
},
mapper,
f.CategoryExpander(),
).AddError(err)
}
@@ -402,6 +405,13 @@ func (f *TestFactory) ClientSet() (internalclientset.Interface, error) {
return clientset, nil
}
func (f *TestFactory) DynamicClient() (dynamic.DynamicInterface, error) {
if f.FakeDynamicClient != nil {
return f.FakeDynamicClient, nil
}
return f.Factory.DynamicClient()
}
func (f *TestFactory) RESTClient() (*restclient.RESTClient, error) {
// Swap out the HTTP client out of the client with the fake's version.
fakeClient := f.Client.(*fake.RESTClient)

View File

@@ -66,7 +66,6 @@ type EditOptions struct {
cmdutil.ValidateOptions
ResourceMapper *resource.Mapper
OriginalResult *resource.Result
EditMode EditMode

View File

@@ -33,6 +33,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
scaleclient "k8s.io/client-go/scale"
@@ -92,6 +93,9 @@ type ClientAccessFactory interface {
// ClientSet gives you back an internal, generated clientset
ClientSet() (internalclientset.Interface, error)
// DynamicClient returns a dynamic client ready for use
DynamicClient() (dynamic.DynamicInterface, error)
// KubernetesClientSet gives you back an external clientset
KubernetesClientSet() (*kubernetes.Clientset, error)

View File

@@ -22,7 +22,6 @@ import (
"os"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/dynamic"
scaleclient "k8s.io/client-go/scale"
"k8s.io/kubernetes/pkg/kubectl"
@@ -46,24 +45,12 @@ func NewBuilderFactory(clientAccessFactory ClientAccessFactory, objectMappingFac
// NewBuilder returns a new resource builder for structured api objects.
func (f *ring2Factory) NewBuilder() *resource.Builder {
clientMapperFunc := resource.ClientMapperFunc(f.objectMappingFactory.ClientForMapping)
mapper, mapperErr := f.objectMappingFactory.RESTMapper()
unstructuredClientMapperFunc := resource.ClientMapperFunc(f.objectMappingFactory.UnstructuredClientForMapping)
categoryExpander := f.objectMappingFactory.CategoryExpander()
return resource.NewBuilder(
&resource.Mapper{
RESTMapper: mapper,
ClientMapper: clientMapperFunc,
Decoder: InternalVersionDecoder(),
},
&resource.Mapper{
RESTMapper: mapper,
ClientMapper: unstructuredClientMapperFunc,
Decoder: unstructured.UnstructuredJSONScheme,
},
f.clientAccessFactory.ClientConfig,
mapper,
categoryExpander,
).AddError(mapperErr)
}

View File

@@ -52,6 +52,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
utilflag "k8s.io/apiserver/pkg/util/flag"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
@@ -199,6 +200,13 @@ func (f *ring0Factory) ClientSet() (internalclientset.Interface, error) {
return internalclientset.NewForConfig(clientConfig)
}
func (f *ring0Factory) DynamicClient() (dynamic.DynamicInterface, error) {
clientConfig, err := f.ClientConfig()
if err != nil {
return nil, err
}
return dynamic.NewForConfig(clientConfig)
}
func (f *ring0Factory) checkMatchingServerVersion() error {
f.checkServerVersion.Do(func() {
if !f.requireMatchedServerVersion {
@@ -249,6 +257,7 @@ func (f *ring0Factory) UpdatePodSpecForObject(obj runtime.Object, fn func(*v1.Po
t.Spec.Template = &v1.PodTemplateSpec{}
}
return true, fn(&t.Spec.Template.Spec)
// Deployment
case *extensionsv1beta1.Deployment:
return true, fn(&t.Spec.Template.Spec)
@@ -258,6 +267,7 @@ func (f *ring0Factory) UpdatePodSpecForObject(obj runtime.Object, fn func(*v1.Po
return true, fn(&t.Spec.Template.Spec)
case *appsv1.Deployment:
return true, fn(&t.Spec.Template.Spec)
// DaemonSet
case *extensionsv1beta1.DaemonSet:
return true, fn(&t.Spec.Template.Spec)
@@ -265,6 +275,7 @@ func (f *ring0Factory) UpdatePodSpecForObject(obj runtime.Object, fn func(*v1.Po
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)
@@ -272,6 +283,7 @@ func (f *ring0Factory) UpdatePodSpecForObject(obj runtime.Object, fn func(*v1.Po
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)
@@ -279,14 +291,17 @@ func (f *ring0Factory) UpdatePodSpecForObject(obj runtime.Object, fn func(*v1.Po
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)
// CronJob
case *batchv1beta1.CronJob:
return true, fn(&t.Spec.JobTemplate.Spec.Template.Spec)
case *batchv2alpha1.CronJob:
return true, fn(&t.Spec.JobTemplate.Spec.Template.Spec)
default:
return false, fmt.Errorf("the object is not a pod or does not have a pod template")
}

View File

@@ -27,7 +27,13 @@ import (
"sync"
"time"
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"
corev1 "k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
@@ -38,6 +44,7 @@ import (
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core"
apiv1 "k8s.io/kubernetes/pkg/apis/core/v1"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/kubectl"
@@ -198,6 +205,8 @@ func (f *ring1Factory) LogsForObject(object, options runtime.Object, timeout tim
switch t := object.(type) {
case *api.Pod:
return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil
case *corev1.Pod:
return clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil
}
namespace, selector, err := selectorsForObject(object)
@@ -223,10 +232,31 @@ func selectorsForObject(object runtime.Object) (namespace string, selector label
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *extensionsv1beta1.ReplicaSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1.ReplicaSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta2.ReplicaSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *api.ReplicationController:
namespace = t.Namespace
selector = labels.SelectorFromSet(t.Spec.Selector)
case *corev1.ReplicationController:
namespace = t.Namespace
selector = labels.SelectorFromSet(t.Spec.Selector)
case *apps.StatefulSet:
namespace = t.Namespace
@@ -234,6 +264,24 @@ func selectorsForObject(object runtime.Object) (namespace string, selector label
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1.StatefulSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta1.StatefulSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta2.StatefulSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *extensions.DaemonSet:
namespace = t.Namespace
@@ -241,6 +289,24 @@ func selectorsForObject(object runtime.Object) (namespace string, selector label
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *extensionsv1beta1.DaemonSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1.DaemonSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta2.DaemonSet:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *extensions.Deployment:
namespace = t.Namespace
@@ -248,6 +314,30 @@ func selectorsForObject(object runtime.Object) (namespace string, selector label
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *extensionsv1beta1.Deployment:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1.Deployment:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta1.Deployment:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *appsv1beta2.Deployment:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *batch.Job:
namespace = t.Namespace
@@ -255,6 +345,12 @@ func selectorsForObject(object runtime.Object) (namespace string, selector label
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *batchv1.Job:
namespace = t.Namespace
selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return "", nil, fmt.Errorf("invalid label selector: %v", err)
}
case *api.Service:
namespace = t.Namespace
@@ -262,6 +358,12 @@ func selectorsForObject(object runtime.Object) (namespace string, selector label
return "", nil, fmt.Errorf("invalid service '%s': Service is defined without a selector", t.Name)
}
selector = labels.SelectorFromSet(t.Spec.Selector)
case *corev1.Service:
namespace = t.Namespace
if t.Spec.Selector == nil || len(t.Spec.Selector) == 0 {
return "", nil, fmt.Errorf("invalid service '%s': Service is defined without a selector", t.Name)
}
selector = labels.SelectorFromSet(t.Spec.Selector)
default:
return "", nil, fmt.Errorf("selector for %T not implemented", object)
@@ -325,6 +427,11 @@ func (f *ring1Factory) AttachablePodForObject(object runtime.Object, timeout tim
switch t := object.(type) {
case *api.Pod:
return t, nil
case *corev1.Pod:
internalPod := &api.Pod{}
err := apiv1.Convert_v1_Pod_To_core_Pod(t, internalPod, nil)
return internalPod, err
}
namespace, selector, err := selectorsForObject(object)

View File

@@ -25,7 +25,6 @@ import (
"k8s.io/api/core/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/api/meta/testrestmapper"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
@@ -36,7 +35,6 @@ import (
manualfake "k8s.io/client-go/rest/fake"
testcore "k8s.io/client-go/testing"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/testapi"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/controller"
@@ -442,10 +440,10 @@ func TestMakePortsString(t *testing.T) {
}
}
func fakeClient() resource.ClientMapper {
return resource.ClientMapperFunc(func(*meta.RESTMapping) (resource.RESTClient, error) {
func fakeClient() resource.FakeClientFunc {
return func(version schema.GroupVersion) (resource.RESTClient, error) {
return &manualfake.RESTClient{}, nil
})
}
}
func TestDiscoveryReplaceAliases(t *testing.T) {
@@ -473,15 +471,7 @@ func TestDiscoveryReplaceAliases(t *testing.T) {
ds := &fakeDiscoveryClient{}
mapper := NewShortcutExpander(testrestmapper.TestOnlyStaticRESTMapper(legacyscheme.Registry, legacyscheme.Scheme), ds)
b := resource.NewBuilder(
&resource.Mapper{
RESTMapper: mapper,
ClientMapper: fakeClient(),
Decoder: testapi.Default.Codec(),
},
nil,
categories.LegacyCategoryExpander,
)
b := resource.NewFakeBuilder(fakeClient(), mapper, categories.LegacyCategoryExpander)
for _, test := range tests {
replaced := b.ReplaceAliases(test.arg)

View File

@@ -8,6 +8,7 @@ go_library(
name = "go_default_library",
srcs = [
"builder.go",
"client.go",
"doc.go",
"helper.go",
"interfaces.go",
@@ -29,16 +30,19 @@ go_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/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/unstructuredscheme:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels: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/serializer:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/client-go/dynamic:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
],
)
@@ -56,10 +60,10 @@ go_test(
],
embed = [":go_default_library"],
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/core/install:go_default_library",
"//pkg/kubectl/categories:go_default_library",
"//pkg/kubectl/scheme:go_default_library",
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
"//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",

View File

@@ -26,10 +26,12 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructuredscheme"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/kubectl/categories"
@@ -48,14 +50,22 @@ type Builder struct {
categoryExpander categories.CategoryExpander
// mapper is set explicitly by resource builders
mapper *Mapper
internal *Mapper
unstructured *Mapper
mapper *mapper
internal *mapper
unstructured *mapper
// clientConfigFn is a function to produce a client, *if* you need one
clientConfigFn ClientConfigFunc
restMapper meta.RESTMapper
// objectTyper is statically determinant per-command invocation based on your internal or unstructured choice
// it does not ever need to rely upon discovery.
objectTyper runtime.ObjectTyper
// codecFactory describes which codecs you want to use
negotiatedSerializer runtime.NegotiatedSerializer
// local indicates that we cannot make server calls
local bool
@@ -96,6 +106,9 @@ type Builder struct {
export bool
schema validation.Schema
// fakeClientFn is used for testing
fakeClientFn FakeClientFunc
}
var missingResourceError = fmt.Errorf(`You must provide one or more resources by argument or filename.
@@ -128,13 +141,22 @@ type resourceTuple struct {
Name string
}
type FakeClientFunc func(version schema.GroupVersion) (RESTClient, error)
func NewFakeBuilder(fakeClientFn FakeClientFunc, restMapper meta.RESTMapper, categoryExpander categories.CategoryExpander) *Builder {
ret := NewBuilder(nil, restMapper, categoryExpander)
ret.fakeClientFn = fakeClientFn
return ret
}
// NewBuilder creates a builder that operates on generic objects. At least one of
// internal or unstructured must be specified.
// TODO: Add versioned client (although versioned is still lossy)
func NewBuilder(internal, unstructured *Mapper, categoryExpander categories.CategoryExpander) *Builder {
// TODO remove internal and unstructured mapper and instead have them set the negotiated serializer for use in the client
func NewBuilder(clientConfigFn ClientConfigFunc, restMapper meta.RESTMapper, categoryExpander categories.CategoryExpander) *Builder {
return &Builder{
internal: internal,
unstructured: unstructured,
clientConfigFn: clientConfigFn,
restMapper: restMapper,
categoryExpander: categoryExpander,
requireObject: true,
}
@@ -194,39 +216,45 @@ func (b *Builder) FilenameParam(enforceNamespace bool, filenameOptions *Filename
// reads and then writes an object. Use this mode in preference to Internal unless you
// are working with Go types directly.
func (b *Builder) Unstructured() *Builder {
if b.unstructured == nil {
b.errs = append(b.errs, fmt.Errorf("no unstructured mapper provided"))
return b
}
if b.mapper != nil && b.mapper != b.unstructured {
if b.mapper != nil {
b.errs = append(b.errs, fmt.Errorf("another mapper was already selected, cannot use unstructured types"))
return b
}
b.mapper = b.unstructured
b.mapper.localFn = b.isLocal
b.objectTyper = unstructuredscheme.NewUnstructuredObjectTyper()
b.mapper = &mapper{
localFn: b.isLocal,
restMapper: b.restMapper,
clientFn: b.getClient,
decoder: unstructured.UnstructuredJSONScheme,
}
return b
}
// Internal updates the builder so that it will convert objects off the wire
// into the internal form if necessary. Using internal types is lossy - fields added
// to the server will not be seen by the client code and may result in failure. Only
// use this mode when working offline, or when generating patches to send to the server.
// Use Unstructured if you are reading an object and performing a POST or PUT.
func (b *Builder) Internal(typer runtime.ObjectTyper) *Builder {
if b.internal == nil {
b.errs = append(b.errs, fmt.Errorf("no internal mapper provided"))
return b
}
if b.mapper != nil && b.mapper != b.internal {
// WithScheme uses the scheme to manage typing, conversion (optional), and decoding. If decodingVersions
// is empty, then you can end up with internal types. You have been warned.
func (b *Builder) WithScheme(scheme *runtime.Scheme, decodingVersions ...schema.GroupVersion) *Builder {
if b.mapper != nil {
b.errs = append(b.errs, fmt.Errorf("another mapper was already selected, cannot use internal types"))
return b
}
b.mapper = b.internal
b.mapper.localFn = b.isLocal
b.objectTyper = scheme
codecFactory := serializer.NewCodecFactory(scheme)
negotiatedSerializer := runtime.NegotiatedSerializer(codecFactory)
// if you specified versions, you're specifying a desire for external types, which you don't want to round-trip through
// internal types
if len(decodingVersions) > 0 {
negotiatedSerializer = &serializer.DirectCodecFactory{CodecFactory: codecFactory}
}
b.negotiatedSerializer = negotiatedSerializer
b.mapper = &mapper{
localFn: b.isLocal,
restMapper: b.restMapper,
clientFn: b.getClient,
decoder: codecFactory.UniversalDecoder(decodingVersions...),
}
b.objectTyper = typer
return b
}
@@ -241,9 +269,6 @@ func (b *Builder) LocalParam(local bool) *Builder {
// Local will avoid asking the server for results.
func (b *Builder) Local() *Builder {
b.local = true
mapper := *b.mapper
mapper.ClientMapper = DisabledClientForMapping{ClientMapper: mapper.ClientMapper}
b.mapper = &mapper
return b
}
@@ -252,7 +277,7 @@ func (b *Builder) isLocal() bool {
}
// Mapper returns a copy of the current mapper.
func (b *Builder) Mapper() *Mapper {
func (b *Builder) Mapper() *mapper {
mapper := *b.mapper
return &mapper
}
@@ -636,13 +661,13 @@ func (b *Builder) mappingFor(resourceOrKindArg string) (*meta.RESTMapping, error
fullySpecifiedGVR, groupResource := schema.ParseResourceArg(resourceOrKindArg)
gvk := schema.GroupVersionKind{}
if fullySpecifiedGVR != nil {
gvk, _ = b.mapper.RESTMapper.KindFor(*fullySpecifiedGVR)
gvk, _ = b.mapper.restMapper.KindFor(*fullySpecifiedGVR)
}
if gvk.Empty() {
gvk, _ = b.mapper.RESTMapper.KindFor(groupResource.WithVersion(""))
gvk, _ = b.mapper.restMapper.KindFor(groupResource.WithVersion(""))
}
if !gvk.Empty() {
return b.mapper.RESTMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
return b.mapper.restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
}
fullySpecifiedGVK, groupKind := schema.ParseKindArg(resourceOrKindArg)
@@ -652,12 +677,12 @@ func (b *Builder) mappingFor(resourceOrKindArg string) (*meta.RESTMapping, error
}
if !fullySpecifiedGVK.Empty() {
if mapping, err := b.mapper.RESTMapper.RESTMapping(fullySpecifiedGVK.GroupKind(), fullySpecifiedGVK.Version); err == nil {
if mapping, err := b.mapper.restMapper.RESTMapping(fullySpecifiedGVK.GroupKind(), fullySpecifiedGVK.Version); err == nil {
return mapping, nil
}
}
mapping, err := b.mapper.RESTMapper.RESTMapping(groupKind, gvk.Version)
mapping, err := b.mapper.restMapper.RESTMapping(groupKind, gvk.Version)
if err != nil {
// if we error out here, it is because we could not match a resource or a kind
// for the given argument. To maintain consistency with previous behavior,
@@ -782,7 +807,7 @@ func (b *Builder) visitBySelector() *Result {
visitors := []Visitor{}
for _, mapping := range mappings {
client, err := b.mapper.ClientMapper.ClientForMapping(mapping)
client, err := b.getClient(mapping.GroupVersionKind.GroupVersion())
if err != nil {
result.err = err
return result
@@ -803,6 +828,18 @@ func (b *Builder) visitBySelector() *Result {
return result
}
func (b *Builder) getClient(gv schema.GroupVersion) (RESTClient, error) {
if b.fakeClientFn != nil {
return b.fakeClientFn(gv)
}
if b.negotiatedSerializer != nil {
return b.clientConfigFn.clientForGroupVersion(gv, b.negotiatedSerializer)
}
return b.clientConfigFn.unstructuredClientForGroupVersion(gv)
}
func (b *Builder) visitByResource() *Result {
// if b.singleItemImplied is false, this could be by default, so double-check length
// of resourceTuples to determine if in fact it is singleItemImplied or not
@@ -832,7 +869,7 @@ func (b *Builder) visitByResource() *Result {
if _, ok := clients[s]; ok {
continue
}
client, err := b.mapper.ClientMapper.ClientForMapping(mapping)
client, err := b.getClient(mapping.GroupVersionKind.GroupVersion())
if err != nil {
result.err = err
return result
@@ -910,7 +947,7 @@ func (b *Builder) visitByName() *Result {
}
mapping := mappings[0]
client, err := b.mapper.ClientMapper.ClientForMapping(mapping)
client, err := b.getClient(mapping.GroupVersionKind.GroupVersion())
if err != nil {
result.err = err
return result

View File

@@ -47,11 +47,11 @@ import (
"k8s.io/client-go/rest/fake"
restclientwatch "k8s.io/client-go/rest/watch"
utiltesting "k8s.io/client-go/util/testing"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl/categories"
"k8s.io/kubernetes/pkg/kubectl/scheme"
// install the pod scheme into the legacy scheme for test typer resolution
"github.com/davecgh/go-spew/spew"
_ "k8s.io/kubernetes/pkg/apis/core/install"
)
@@ -76,14 +76,14 @@ func watchBody(events ...watch.Event) string {
return buf.String()
}
func fakeClient() ClientMapper {
return ClientMapperFunc(func(*meta.RESTMapping) (RESTClient, error) {
func fakeClient() FakeClientFunc {
return func(version schema.GroupVersion) (RESTClient, error) {
return &fake.RESTClient{}, nil
})
}
}
func fakeClientWith(testName string, t *testing.T, data map[string]string) ClientMapper {
return ClientMapperFunc(func(*meta.RESTMapping) (RESTClient, error) {
func fakeClientWith(testName string, t *testing.T, data map[string]string) FakeClientFunc {
return func(version schema.GroupVersion) (RESTClient, error) {
return &fake.RESTClient{
GroupVersion: corev1GV,
NegotiatedSerializer: serializer.DirectCodecFactory{CodecFactory: scheme.Codecs},
@@ -106,7 +106,7 @@ func fakeClientWith(testName string, t *testing.T, data map[string]string) Clien
}, nil
}),
}, nil
})
}
}
func testData() (*v1.PodList, *v1.ServiceList) {
@@ -272,16 +272,9 @@ func newDefaultBuilder() *Builder {
return newDefaultBuilderWith(fakeClient())
}
func newDefaultBuilderWith(client ClientMapper) *Builder {
return NewBuilder(
&Mapper{
RESTMapper: restmapper,
ClientMapper: client,
Decoder: corev1Codec,
},
nil,
categories.LegacyCategoryExpander,
).Internal(legacyscheme.Scheme)
func newDefaultBuilderWith(fakeClientFn FakeClientFunc) *Builder {
return NewFakeBuilder(fakeClientFn, restmapper, categories.LegacyCategoryExpander).
WithScheme(scheme.Scheme, scheme.Registry.RegisteredGroupVersions()...)
}
func TestPathBuilderAndVersionedObjectNotDefaulted(t *testing.T) {
@@ -407,11 +400,11 @@ func TestPathBuilderWithMultiple(t *testing.T) {
switch test.object.(type) {
case *v1.Pod:
if _, ok := v.Object.(*v1.Pod); !ok || v.Name != test.expectedNames[i] || v.Namespace != "test" {
t.Errorf("unexpected info: %#v", v)
t.Errorf("unexpected info: %v", spew.Sdump(v.Object))
}
case *v1.ReplicationController:
if _, ok := v.Object.(*v1.ReplicationController); !ok || v.Name != test.expectedNames[i] || v.Namespace != "test" {
t.Errorf("unexpected info: %#v", v)
t.Errorf("unexpected info: %v", spew.Sdump(v.Object))
}
}
}

View File

@@ -0,0 +1,59 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package resource
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
)
// TODO require negotiatedSerializer. leaving it optional lets us plumb current behavior and deal with the difference after major plumbing is complete
func (clientConfigFn ClientConfigFunc) clientForGroupVersion(gv schema.GroupVersion, negotiatedSerializer runtime.NegotiatedSerializer) (RESTClient, error) {
cfg, err := clientConfigFn()
if err != nil {
return nil, err
}
if negotiatedSerializer != nil {
cfg.ContentConfig.NegotiatedSerializer = negotiatedSerializer
}
cfg.GroupVersion = &gv
if len(gv.Group) == 0 {
cfg.APIPath = "/api"
} else {
cfg.APIPath = "/apis"
}
return rest.RESTClientFor(cfg)
}
func (clientConfigFn ClientConfigFunc) unstructuredClientForGroupVersion(gv schema.GroupVersion) (RESTClient, error) {
cfg, err := clientConfigFn()
if err != nil {
return nil, err
}
cfg.ContentConfig = dynamic.ContentConfig()
cfg.GroupVersion = &gv
if len(gv.Group) == 0 {
cfg.APIPath = "/api"
} else {
cfg.APIPath = "/apis"
}
return rest.RESTClientFor(cfg)
}

View File

@@ -17,38 +17,24 @@ limitations under the License.
package resource
import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/types"
client "k8s.io/client-go/rest"
"k8s.io/client-go/rest"
)
type RESTMapperFunc func() (meta.RESTMapper, error)
type ClientConfigFunc func() (*rest.Config, error)
// RESTClient is a client helper for dealing with RESTful resources
// in a generic way.
type RESTClient interface {
Get() *client.Request
Post() *client.Request
Patch(types.PatchType) *client.Request
Delete() *client.Request
Put() *client.Request
}
// ClientMapper abstracts retrieving a Client for mapped objects.
type ClientMapper interface {
ClientForMapping(mapping *meta.RESTMapping) (RESTClient, error)
}
// ClientMapperFunc implements ClientMapper for a function
type ClientMapperFunc func(mapping *meta.RESTMapping) (RESTClient, error)
// ClientForMapping implements ClientMapper
func (f ClientMapperFunc) ClientForMapping(mapping *meta.RESTMapping) (RESTClient, error) {
return f(mapping)
Get() *rest.Request
Post() *rest.Request
Patch(types.PatchType) *rest.Request
Delete() *rest.Request
Put() *rest.Request
}
// RequestTransform is a function that is given a chance to modify the outgoing request.
type RequestTransform func(*client.Request)
type RequestTransform func(*rest.Request)
// NewClientWithOptions wraps the provided RESTClient and invokes each transform on each
// newly created request.
@@ -61,26 +47,26 @@ type clientOptions struct {
transforms []RequestTransform
}
func (c *clientOptions) modify(req *client.Request) *client.Request {
func (c *clientOptions) modify(req *rest.Request) *rest.Request {
for _, transform := range c.transforms {
transform(req)
}
return req
}
func (c *clientOptions) Get() *client.Request {
func (c *clientOptions) Get() *rest.Request {
return c.modify(c.c.Get())
}
func (c *clientOptions) Post() *client.Request {
func (c *clientOptions) Post() *rest.Request {
return c.modify(c.c.Post())
}
func (c *clientOptions) Patch(t types.PatchType) *client.Request {
func (c *clientOptions) Patch(t types.PatchType) *rest.Request {
return c.modify(c.c.Patch(t))
}
func (c *clientOptions) Delete() *client.Request {
func (c *clientOptions) Delete() *rest.Request {
return c.modify(c.c.Delete())
}
func (c *clientOptions) Put() *client.Request {
func (c *clientOptions) Put() *rest.Request {
return c.modify(c.c.Put())
}

View File

@@ -27,20 +27,20 @@ import (
// Mapper is a convenience struct for holding references to the interfaces
// needed to create Info for arbitrary objects.
type Mapper struct {
type mapper struct {
// localFn indicates the call can't make server requests
localFn func() bool
RESTMapper meta.RESTMapper
ClientMapper ClientMapper
Decoder runtime.Decoder
restMapper meta.RESTMapper
clientFn func(version schema.GroupVersion) (RESTClient, error)
decoder runtime.Decoder
}
// InfoForData creates an Info object for the given data. An error is returned
// if any of the decoding or client lookup steps fail. Name and namespace will be
// set into Info if the mapping's MetadataAccessor can retrieve them.
func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) {
obj, gvk, err := m.Decoder.Decode(data, nil, nil)
func (m *mapper) infoForData(data []byte, source string) (*Info, error) {
obj, gvk, err := m.decoder.Decode(data, nil, nil)
if err != nil {
return nil, fmt.Errorf("unable to decode %q: %v", source, err)
}
@@ -59,13 +59,13 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) {
}
if m.localFn == nil || !m.localFn() {
mapping, err := m.RESTMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
mapping, err := m.restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
return nil, fmt.Errorf("unable to recognize %q: %v", source, err)
}
ret.Mapping = mapping
client, err := m.ClientMapper.ClientForMapping(mapping)
client, err := m.clientFn(gvk.GroupVersion())
if err != nil {
return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err)
}
@@ -78,7 +78,7 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) {
// InfoForObject creates an Info object for the given Object. An error is returned
// if the object cannot be introspected. Name and namespace will be set into Info
// if the mapping's MetadataAccessor can retrieve them.
func (m *Mapper) InfoForObject(obj runtime.Object, typer runtime.ObjectTyper, preferredGVKs []schema.GroupVersionKind) (*Info, error) {
func (m *mapper) infoForObject(obj runtime.Object, typer runtime.ObjectTyper, preferredGVKs []schema.GroupVersionKind) (*Info, error) {
groupVersionKinds, _, err := typer.ObjectKinds(obj)
if err != nil {
return nil, fmt.Errorf("unable to get type info from the object %q: %v", reflect.TypeOf(obj), err)
@@ -101,13 +101,13 @@ func (m *Mapper) InfoForObject(obj runtime.Object, typer runtime.ObjectTyper, pr
}
if m.localFn == nil || !m.localFn() {
mapping, err := m.RESTMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
mapping, err := m.restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
return nil, fmt.Errorf("unable to recognize %v", err)
}
ret.Mapping = mapping
client, err := m.ClientMapper.ClientForMapping(mapping)
client, err := m.clientFn(gvk.GroupVersion())
if err != nil {
return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err)
}
@@ -152,13 +152,3 @@ func preferredObjectKind(possibilities []schema.GroupVersionKind, preferences []
// Just pick the first
return possibilities[0]
}
// DisabledClientForMapping allows callers to avoid allowing remote calls when handling
// resources.
type DisabledClientForMapping struct {
ClientMapper
}
func (f DisabledClientForMapping) ClientForMapping(mapping *meta.RESTMapping) (RESTClient, error) {
return nil, nil
}

View File

@@ -42,7 +42,7 @@ type Result struct {
singleItemImplied bool
targetsSingleItems bool
mapper *Mapper
mapper *mapper
ignoreErrors []utilerrors.Matcher
// populated by a call to Infos
@@ -77,7 +77,7 @@ func (r *Result) IgnoreErrors(fns ...ErrMatchFunc) *Result {
}
// Mapper returns a copy of the builder's mapper.
func (r *Result) Mapper() *Mapper {
func (r *Result) Mapper() *mapper {
return r.mapper
}

View File

@@ -372,12 +372,12 @@ func (v ContinueOnErrorVisitor) Visit(fn VisitorFunc) error {
type FlattenListVisitor struct {
visitor Visitor
typer runtime.ObjectTyper
mapper *Mapper
mapper *mapper
}
// NewFlattenListVisitor creates a visitor that will expand list style runtime.Objects
// into individual items and then visit them individually.
func NewFlattenListVisitor(v Visitor, typer runtime.ObjectTyper, mapper *Mapper) Visitor {
func NewFlattenListVisitor(v Visitor, typer runtime.ObjectTyper, mapper *mapper) Visitor {
return FlattenListVisitor{v, typer, mapper}
}
@@ -393,7 +393,7 @@ func (v FlattenListVisitor) Visit(fn VisitorFunc) error {
if err != nil {
return fn(info, nil)
}
if errs := runtime.DecodeList(items, v.mapper.Decoder); len(errs) > 0 {
if errs := runtime.DecodeList(items, v.mapper.decoder); len(errs) > 0 {
return utilerrors.NewAggregate(errs)
}
@@ -404,7 +404,7 @@ func (v FlattenListVisitor) Visit(fn VisitorFunc) error {
}
for i := range items {
item, err := v.mapper.InfoForObject(items[i], v.typer, preferredGVKs)
item, err := v.mapper.infoForObject(items[i], v.typer, preferredGVKs)
if err != nil {
return err
}
@@ -433,7 +433,7 @@ func ignoreFile(path string, extensions []string) bool {
}
// FileVisitorForSTDIN return a special FileVisitor just for STDIN
func FileVisitorForSTDIN(mapper *Mapper, schema validation.Schema) Visitor {
func FileVisitorForSTDIN(mapper *mapper, schema validation.Schema) Visitor {
return &FileVisitor{
Path: constSTDINstr,
StreamVisitor: NewStreamVisitor(nil, mapper, constSTDINstr, schema),
@@ -443,7 +443,7 @@ func FileVisitorForSTDIN(mapper *Mapper, schema validation.Schema) Visitor {
// ExpandPathsToFileVisitors will return a slice of FileVisitors that will handle files from the provided path.
// After FileVisitors open the files, they will pass an io.Reader to a StreamVisitor to do the reading. (stdin
// is also taken care of). Paths argument also accepts a single file, and will return a single visitor
func ExpandPathsToFileVisitors(mapper *Mapper, paths string, recursive bool, extensions []string, schema validation.Schema) ([]Visitor, error) {
func ExpandPathsToFileVisitors(mapper *mapper, paths string, recursive bool, extensions []string, schema validation.Schema) ([]Visitor, error) {
var visitors []Visitor
err := filepath.Walk(paths, func(path string, fi os.FileInfo, err error) error {
if err != nil {
@@ -510,17 +510,17 @@ func (v *FileVisitor) Visit(fn VisitorFunc) error {
// a stream decoder method on runtime.Codec to properly handle this.
type StreamVisitor struct {
io.Reader
*Mapper
*mapper
Source string
Schema validation.Schema
}
// NewStreamVisitor is a helper function that is useful when we want to change the fields of the struct but keep calls the same.
func NewStreamVisitor(r io.Reader, mapper *Mapper, source string, schema validation.Schema) *StreamVisitor {
func NewStreamVisitor(r io.Reader, mapper *mapper, source string, schema validation.Schema) *StreamVisitor {
return &StreamVisitor{
Reader: r,
Mapper: mapper,
mapper: mapper,
Source: source,
Schema: schema,
}
@@ -545,7 +545,7 @@ func (v *StreamVisitor) Visit(fn VisitorFunc) error {
if err := ValidateSchema(ext.Raw, v.Schema); err != nil {
return fmt.Errorf("error validating %q: %v", v.Source, err)
}
info, err := v.InfoForData(ext.Raw, v.Source)
info, err := v.infoForData(ext.Raw, v.Source)
if err != nil {
if fnErr := fn(info, err); fnErr != nil {
return fnErr