simplify resource builder usage

pull/8/head
David Eads 2018-04-27 11:38:34 -04:00
parent 54cf942a05
commit b8aa7baa7d
47 changed files with 403 additions and 358 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.GroupVersionKind.GroupVersion().WithResource(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.GroupVersionKind.GroupVersion().WithResource(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.GroupVersionKind.GroupVersion().WithResource(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.GroupVersionKind.GroupVersion().WithResource(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

@ -174,7 +174,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.GroupVersionKind.GroupVersion().WithResource(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

@ -33,7 +33,6 @@ go_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/meta: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/types:go_default_library",

View File

@ -241,7 +241,7 @@ func (o *EnvOptions) RunEnv() error {
if len(o.From) != 0 {
b := o.builder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
LocalParam(o.Local).
ContinueOnError().
NamespaceParam(o.namespace).DefaultNamespace().
@ -309,7 +309,7 @@ func (o *EnvOptions) RunEnv() error {
}
b := o.builder().
Internal(legacyscheme.Scheme).
WithScheme(legacyscheme.Scheme).
LocalParam(o.Local).
ContinueOnError().
NamespaceParam(o.namespace).DefaultNamespace().

View File

@ -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(legacyscheme.Scheme).
LocalParam(o.Local).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().

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 {

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
codecFactory *serializer.CodecFactory
// 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,39 @@ 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)
b.codecFactory = &codecFactory
b.mapper = &mapper{
localFn: b.isLocal,
restMapper: b.restMapper,
clientFn: b.getClient,
decoder: b.codecFactory.UniversalDecoder(decodingVersions...),
}
b.objectTyper = typer
return b
}
@ -241,9 +263,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 +271,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 +655,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 +671,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 +801,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 +822,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.codecFactory != nil {
return b.clientConfigFn.clientForGroupVersion(gv, b.codecFactory)
}
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 +863,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 +941,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