From cf03503216c704ac64ed250ce36cd5a4e6808a8e Mon Sep 17 00:00:00 2001 From: Rishabh Bohra Date: Fri, 22 Oct 2021 03:19:59 +0530 Subject: [PATCH] feat: Collect custom resources (#447) * feat: Collect custom resources Co-authored-by: Martin Hrabovcin Co-authored-by: Andrew Reed --- go.mod | 1 + go.sum | 4 + .../v1beta2/fake/fake_hostcollector.go | 141 +++++++++++ .../v1beta2/fake/fake_remotecollector.go | 141 +++++++++++ .../v1beta2/fake/fake_troubleshoot_client.go | 8 + .../v1beta2/generated_expansion.go | 4 + .../troubleshoot/v1beta2/hostcollector.go | 194 +++++++++++++++ .../troubleshoot/v1beta2/remotecollector.go | 194 +++++++++++++++ .../v1beta2/troubleshoot_client.go | 10 + pkg/collect/cluster_resources.go | 228 ++++++++++++++++++ pkg/redact/literal.go | 2 +- pkg/redact/multi_line.go | 2 +- pkg/redact/redact.go | 38 ++- pkg/redact/redact_test.go | 2 +- pkg/redact/single_line.go | 2 +- pkg/redact/single_line_test.go | 3 +- pkg/redact/yaml.go | 14 +- pkg/redact/yaml_test.go | 2 +- 18 files changed, 981 insertions(+), 9 deletions(-) create mode 100644 pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_hostcollector.go create mode 100644 pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_remotecollector.go create mode 100644 pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/hostcollector.go create mode 100644 pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/remotecollector.go diff --git a/go.mod b/go.mod index 4692e5dc..862502fc 100644 --- a/go.mod +++ b/go.mod @@ -51,6 +51,7 @@ require ( k8s.io/cli-runtime v0.20.2 k8s.io/client-go v0.20.2 sigs.k8s.io/controller-runtime v0.8.3 + sigs.k8s.io/controller-tools v0.7.0 // indirect ) replace ( diff --git a/go.sum b/go.sum index c6456960..264fb84f 100644 --- a/go.sum +++ b/go.sum @@ -390,6 +390,8 @@ github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoM github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= +github.com/gobuffalo/flect v0.2.3 h1:f/ZukRnSNA/DUpSNDadko7Qc0PhGvsew35p/2tu+CRY= +github.com/gobuffalo/flect v0.2.3/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4= @@ -1664,6 +1666,8 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/controller-tools v0.7.0 h1:iZIz1vEcavyEfxjcTLs1WH/MPf4vhPCtTKhoHqV8/G0= +sigs.k8s.io/controller-tools v0.7.0/go.mod h1:bpBAo0VcSDDLuWt47evLhMLPxRPxMDInTEH/YbdeMK0= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/sig-storage-lib-external-provisioner v4.0.1+incompatible/go.mod h1:qhqLyNwJC49PoUalmtzYb4s9fT8HOMBTLbTY1QoVOqI= diff --git a/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_hostcollector.go b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_hostcollector.go new file mode 100644 index 00000000..837c7576 --- /dev/null +++ b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_hostcollector.go @@ -0,0 +1,141 @@ +/* +Copyright 2019 Replicated, Inc.. + +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. +*/ +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeHostCollectors implements HostCollectorInterface +type FakeHostCollectors struct { + Fake *FakeTroubleshootV1beta2 + ns string +} + +var hostcollectorsResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "hostcollectors"} + +var hostcollectorsKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "HostCollector"} + +// Get takes name of the hostCollector, and returns the corresponding hostCollector object, and an error if there is any. +func (c *FakeHostCollectors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.HostCollector, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(hostcollectorsResource, c.ns, name), &v1beta2.HostCollector{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.HostCollector), err +} + +// List takes label and field selectors, and returns the list of HostCollectors that match those selectors. +func (c *FakeHostCollectors) List(ctx context.Context, opts v1.ListOptions) (result *v1beta2.HostCollectorList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(hostcollectorsResource, hostcollectorsKind, c.ns, opts), &v1beta2.HostCollectorList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1beta2.HostCollectorList{ListMeta: obj.(*v1beta2.HostCollectorList).ListMeta} + for _, item := range obj.(*v1beta2.HostCollectorList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested hostCollectors. +func (c *FakeHostCollectors) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(hostcollectorsResource, c.ns, opts)) + +} + +// Create takes the representation of a hostCollector and creates it. Returns the server's representation of the hostCollector, and an error, if there is any. +func (c *FakeHostCollectors) Create(ctx context.Context, hostCollector *v1beta2.HostCollector, opts v1.CreateOptions) (result *v1beta2.HostCollector, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(hostcollectorsResource, c.ns, hostCollector), &v1beta2.HostCollector{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.HostCollector), err +} + +// Update takes the representation of a hostCollector and updates it. Returns the server's representation of the hostCollector, and an error, if there is any. +func (c *FakeHostCollectors) Update(ctx context.Context, hostCollector *v1beta2.HostCollector, opts v1.UpdateOptions) (result *v1beta2.HostCollector, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(hostcollectorsResource, c.ns, hostCollector), &v1beta2.HostCollector{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.HostCollector), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeHostCollectors) UpdateStatus(ctx context.Context, hostCollector *v1beta2.HostCollector, opts v1.UpdateOptions) (*v1beta2.HostCollector, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(hostcollectorsResource, "status", c.ns, hostCollector), &v1beta2.HostCollector{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.HostCollector), err +} + +// Delete takes name of the hostCollector and deletes it. Returns an error if one occurs. +func (c *FakeHostCollectors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(hostcollectorsResource, c.ns, name), &v1beta2.HostCollector{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeHostCollectors) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(hostcollectorsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1beta2.HostCollectorList{}) + return err +} + +// Patch applies the patch and returns the patched hostCollector. +func (c *FakeHostCollectors) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta2.HostCollector, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(hostcollectorsResource, c.ns, name, pt, data, subresources...), &v1beta2.HostCollector{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.HostCollector), err +} diff --git a/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_remotecollector.go b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_remotecollector.go new file mode 100644 index 00000000..ae0ddb17 --- /dev/null +++ b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_remotecollector.go @@ -0,0 +1,141 @@ +/* +Copyright 2019 Replicated, Inc.. + +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. +*/ +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeRemoteCollectors implements RemoteCollectorInterface +type FakeRemoteCollectors struct { + Fake *FakeTroubleshootV1beta2 + ns string +} + +var remotecollectorsResource = schema.GroupVersionResource{Group: "troubleshoot.sh", Version: "v1beta2", Resource: "remotecollectors"} + +var remotecollectorsKind = schema.GroupVersionKind{Group: "troubleshoot.sh", Version: "v1beta2", Kind: "RemoteCollector"} + +// Get takes name of the remoteCollector, and returns the corresponding remoteCollector object, and an error if there is any. +func (c *FakeRemoteCollectors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.RemoteCollector, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(remotecollectorsResource, c.ns, name), &v1beta2.RemoteCollector{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.RemoteCollector), err +} + +// List takes label and field selectors, and returns the list of RemoteCollectors that match those selectors. +func (c *FakeRemoteCollectors) List(ctx context.Context, opts v1.ListOptions) (result *v1beta2.RemoteCollectorList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(remotecollectorsResource, remotecollectorsKind, c.ns, opts), &v1beta2.RemoteCollectorList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1beta2.RemoteCollectorList{ListMeta: obj.(*v1beta2.RemoteCollectorList).ListMeta} + for _, item := range obj.(*v1beta2.RemoteCollectorList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested remoteCollectors. +func (c *FakeRemoteCollectors) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(remotecollectorsResource, c.ns, opts)) + +} + +// Create takes the representation of a remoteCollector and creates it. Returns the server's representation of the remoteCollector, and an error, if there is any. +func (c *FakeRemoteCollectors) Create(ctx context.Context, remoteCollector *v1beta2.RemoteCollector, opts v1.CreateOptions) (result *v1beta2.RemoteCollector, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(remotecollectorsResource, c.ns, remoteCollector), &v1beta2.RemoteCollector{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.RemoteCollector), err +} + +// Update takes the representation of a remoteCollector and updates it. Returns the server's representation of the remoteCollector, and an error, if there is any. +func (c *FakeRemoteCollectors) Update(ctx context.Context, remoteCollector *v1beta2.RemoteCollector, opts v1.UpdateOptions) (result *v1beta2.RemoteCollector, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(remotecollectorsResource, c.ns, remoteCollector), &v1beta2.RemoteCollector{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.RemoteCollector), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeRemoteCollectors) UpdateStatus(ctx context.Context, remoteCollector *v1beta2.RemoteCollector, opts v1.UpdateOptions) (*v1beta2.RemoteCollector, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(remotecollectorsResource, "status", c.ns, remoteCollector), &v1beta2.RemoteCollector{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.RemoteCollector), err +} + +// Delete takes name of the remoteCollector and deletes it. Returns an error if one occurs. +func (c *FakeRemoteCollectors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(remotecollectorsResource, c.ns, name), &v1beta2.RemoteCollector{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeRemoteCollectors) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(remotecollectorsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1beta2.RemoteCollectorList{}) + return err +} + +// Patch applies the patch and returns the patched remoteCollector. +func (c *FakeRemoteCollectors) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta2.RemoteCollector, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(remotecollectorsResource, c.ns, name, pt, data, subresources...), &v1beta2.RemoteCollector{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta2.RemoteCollector), err +} diff --git a/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_troubleshoot_client.go b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_troubleshoot_client.go index 3e11fbec..3013eb59 100644 --- a/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_troubleshoot_client.go +++ b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_troubleshoot_client.go @@ -35,6 +35,10 @@ func (c *FakeTroubleshootV1beta2) Collectors(namespace string) v1beta2.Collector return &FakeCollectors{c, namespace} } +func (c *FakeTroubleshootV1beta2) HostCollectors(namespace string) v1beta2.HostCollectorInterface { + return &FakeHostCollectors{c, namespace} +} + func (c *FakeTroubleshootV1beta2) HostPreflights(namespace string) v1beta2.HostPreflightInterface { return &FakeHostPreflights{c, namespace} } @@ -47,6 +51,10 @@ func (c *FakeTroubleshootV1beta2) Redactors(namespace string) v1beta2.RedactorIn return &FakeRedactors{c, namespace} } +func (c *FakeTroubleshootV1beta2) RemoteCollectors(namespace string) v1beta2.RemoteCollectorInterface { + return &FakeRemoteCollectors{c, namespace} +} + func (c *FakeTroubleshootV1beta2) SupportBundles(namespace string) v1beta2.SupportBundleInterface { return &FakeSupportBundles{c, namespace} } diff --git a/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/generated_expansion.go b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/generated_expansion.go index 0779c12f..23bd4b83 100644 --- a/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/generated_expansion.go +++ b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/generated_expansion.go @@ -21,10 +21,14 @@ type AnalyzerExpansion interface{} type CollectorExpansion interface{} +type HostCollectorExpansion interface{} + type HostPreflightExpansion interface{} type PreflightExpansion interface{} type RedactorExpansion interface{} +type RemoteCollectorExpansion interface{} + type SupportBundleExpansion interface{} diff --git a/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/hostcollector.go b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/hostcollector.go new file mode 100644 index 00000000..8acec6a9 --- /dev/null +++ b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/hostcollector.go @@ -0,0 +1,194 @@ +/* +Copyright 2019 Replicated, Inc.. + +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. +*/ +// Code generated by client-gen. DO NOT EDIT. + +package v1beta2 + +import ( + "context" + "time" + + v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + scheme "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// HostCollectorsGetter has a method to return a HostCollectorInterface. +// A group's client should implement this interface. +type HostCollectorsGetter interface { + HostCollectors(namespace string) HostCollectorInterface +} + +// HostCollectorInterface has methods to work with HostCollector resources. +type HostCollectorInterface interface { + Create(ctx context.Context, hostCollector *v1beta2.HostCollector, opts v1.CreateOptions) (*v1beta2.HostCollector, error) + Update(ctx context.Context, hostCollector *v1beta2.HostCollector, opts v1.UpdateOptions) (*v1beta2.HostCollector, error) + UpdateStatus(ctx context.Context, hostCollector *v1beta2.HostCollector, opts v1.UpdateOptions) (*v1beta2.HostCollector, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta2.HostCollector, error) + List(ctx context.Context, opts v1.ListOptions) (*v1beta2.HostCollectorList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta2.HostCollector, err error) + HostCollectorExpansion +} + +// hostCollectors implements HostCollectorInterface +type hostCollectors struct { + client rest.Interface + ns string +} + +// newHostCollectors returns a HostCollectors +func newHostCollectors(c *TroubleshootV1beta2Client, namespace string) *hostCollectors { + return &hostCollectors{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the hostCollector, and returns the corresponding hostCollector object, and an error if there is any. +func (c *hostCollectors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.HostCollector, err error) { + result = &v1beta2.HostCollector{} + err = c.client.Get(). + Namespace(c.ns). + Resource("hostcollectors"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of HostCollectors that match those selectors. +func (c *hostCollectors) List(ctx context.Context, opts v1.ListOptions) (result *v1beta2.HostCollectorList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta2.HostCollectorList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("hostcollectors"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested hostCollectors. +func (c *hostCollectors) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("hostcollectors"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a hostCollector and creates it. Returns the server's representation of the hostCollector, and an error, if there is any. +func (c *hostCollectors) Create(ctx context.Context, hostCollector *v1beta2.HostCollector, opts v1.CreateOptions) (result *v1beta2.HostCollector, err error) { + result = &v1beta2.HostCollector{} + err = c.client.Post(). + Namespace(c.ns). + Resource("hostcollectors"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hostCollector). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a hostCollector and updates it. Returns the server's representation of the hostCollector, and an error, if there is any. +func (c *hostCollectors) Update(ctx context.Context, hostCollector *v1beta2.HostCollector, opts v1.UpdateOptions) (result *v1beta2.HostCollector, err error) { + result = &v1beta2.HostCollector{} + err = c.client.Put(). + Namespace(c.ns). + Resource("hostcollectors"). + Name(hostCollector.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hostCollector). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *hostCollectors) UpdateStatus(ctx context.Context, hostCollector *v1beta2.HostCollector, opts v1.UpdateOptions) (result *v1beta2.HostCollector, err error) { + result = &v1beta2.HostCollector{} + err = c.client.Put(). + Namespace(c.ns). + Resource("hostcollectors"). + Name(hostCollector.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(hostCollector). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the hostCollector and deletes it. Returns an error if one occurs. +func (c *hostCollectors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("hostcollectors"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *hostCollectors) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("hostcollectors"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched hostCollector. +func (c *hostCollectors) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta2.HostCollector, err error) { + result = &v1beta2.HostCollector{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("hostcollectors"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/remotecollector.go b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/remotecollector.go new file mode 100644 index 00000000..29138ce0 --- /dev/null +++ b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/remotecollector.go @@ -0,0 +1,194 @@ +/* +Copyright 2019 Replicated, Inc.. + +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. +*/ +// Code generated by client-gen. DO NOT EDIT. + +package v1beta2 + +import ( + "context" + "time" + + v1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + scheme "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// RemoteCollectorsGetter has a method to return a RemoteCollectorInterface. +// A group's client should implement this interface. +type RemoteCollectorsGetter interface { + RemoteCollectors(namespace string) RemoteCollectorInterface +} + +// RemoteCollectorInterface has methods to work with RemoteCollector resources. +type RemoteCollectorInterface interface { + Create(ctx context.Context, remoteCollector *v1beta2.RemoteCollector, opts v1.CreateOptions) (*v1beta2.RemoteCollector, error) + Update(ctx context.Context, remoteCollector *v1beta2.RemoteCollector, opts v1.UpdateOptions) (*v1beta2.RemoteCollector, error) + UpdateStatus(ctx context.Context, remoteCollector *v1beta2.RemoteCollector, opts v1.UpdateOptions) (*v1beta2.RemoteCollector, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta2.RemoteCollector, error) + List(ctx context.Context, opts v1.ListOptions) (*v1beta2.RemoteCollectorList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta2.RemoteCollector, err error) + RemoteCollectorExpansion +} + +// remoteCollectors implements RemoteCollectorInterface +type remoteCollectors struct { + client rest.Interface + ns string +} + +// newRemoteCollectors returns a RemoteCollectors +func newRemoteCollectors(c *TroubleshootV1beta2Client, namespace string) *remoteCollectors { + return &remoteCollectors{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the remoteCollector, and returns the corresponding remoteCollector object, and an error if there is any. +func (c *remoteCollectors) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.RemoteCollector, err error) { + result = &v1beta2.RemoteCollector{} + err = c.client.Get(). + Namespace(c.ns). + Resource("remotecollectors"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of RemoteCollectors that match those selectors. +func (c *remoteCollectors) List(ctx context.Context, opts v1.ListOptions) (result *v1beta2.RemoteCollectorList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta2.RemoteCollectorList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("remotecollectors"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested remoteCollectors. +func (c *remoteCollectors) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("remotecollectors"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a remoteCollector and creates it. Returns the server's representation of the remoteCollector, and an error, if there is any. +func (c *remoteCollectors) Create(ctx context.Context, remoteCollector *v1beta2.RemoteCollector, opts v1.CreateOptions) (result *v1beta2.RemoteCollector, err error) { + result = &v1beta2.RemoteCollector{} + err = c.client.Post(). + Namespace(c.ns). + Resource("remotecollectors"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(remoteCollector). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a remoteCollector and updates it. Returns the server's representation of the remoteCollector, and an error, if there is any. +func (c *remoteCollectors) Update(ctx context.Context, remoteCollector *v1beta2.RemoteCollector, opts v1.UpdateOptions) (result *v1beta2.RemoteCollector, err error) { + result = &v1beta2.RemoteCollector{} + err = c.client.Put(). + Namespace(c.ns). + Resource("remotecollectors"). + Name(remoteCollector.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(remoteCollector). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *remoteCollectors) UpdateStatus(ctx context.Context, remoteCollector *v1beta2.RemoteCollector, opts v1.UpdateOptions) (result *v1beta2.RemoteCollector, err error) { + result = &v1beta2.RemoteCollector{} + err = c.client.Put(). + Namespace(c.ns). + Resource("remotecollectors"). + Name(remoteCollector.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(remoteCollector). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the remoteCollector and deletes it. Returns an error if one occurs. +func (c *remoteCollectors) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("remotecollectors"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *remoteCollectors) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("remotecollectors"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched remoteCollector. +func (c *remoteCollectors) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta2.RemoteCollector, err error) { + result = &v1beta2.RemoteCollector{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("remotecollectors"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/troubleshoot_client.go b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/troubleshoot_client.go index 9dc93fe1..6c3a0fea 100644 --- a/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/troubleshoot_client.go +++ b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/troubleshoot_client.go @@ -27,9 +27,11 @@ type TroubleshootV1beta2Interface interface { RESTClient() rest.Interface AnalyzersGetter CollectorsGetter + HostCollectorsGetter HostPreflightsGetter PreflightsGetter RedactorsGetter + RemoteCollectorsGetter SupportBundlesGetter } @@ -46,6 +48,10 @@ func (c *TroubleshootV1beta2Client) Collectors(namespace string) CollectorInterf return newCollectors(c, namespace) } +func (c *TroubleshootV1beta2Client) HostCollectors(namespace string) HostCollectorInterface { + return newHostCollectors(c, namespace) +} + func (c *TroubleshootV1beta2Client) HostPreflights(namespace string) HostPreflightInterface { return newHostPreflights(c, namespace) } @@ -58,6 +64,10 @@ func (c *TroubleshootV1beta2Client) Redactors(namespace string) RedactorInterfac return newRedactors(c, namespace) } +func (c *TroubleshootV1beta2Client) RemoteCollectors(namespace string) RemoteCollectorInterface { + return newRemoteCollectors(c, namespace) +} + func (c *TroubleshootV1beta2Client) SupportBundles(namespace string) SupportBundleInterface { return newSupportBundles(c, namespace) } diff --git a/pkg/collect/cluster_resources.go b/pkg/collect/cluster_resources.go index 0c128bdc..0b7f126c 100644 --- a/pkg/collect/cluster_resources.go +++ b/pkg/collect/cluster_resources.go @@ -10,12 +10,18 @@ import ( "strings" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + "gopkg.in/yaml.v2" authorizationv1 "k8s.io/api/authorization/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apiextensionsv1clientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" apiextensionsv1beta1clientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -28,6 +34,11 @@ func ClusterResources(c *Collector, clusterResourcesCollector *troubleshootv1bet return nil, err } + dynamicClient, err := dynamic.NewForConfig(c.ClientConfig) + if err != nil { + return nil, err + } + ctx := context.Background() output := NewResult() @@ -113,6 +124,13 @@ func ClusterResources(c *Collector, clusterResourcesCollector *troubleshootv1bet output.SaveResult(c.BundlePath, "cluster-resources/custom-resource-definitions.json", bytes.NewBuffer(customResourceDefinitions)) output.SaveResult(c.BundlePath, "cluster-resources/custom-resource-definitions-errors.json", marshalErrors(crdErrors)) + // crs + customResources, crErrors := crs(ctx, dynamicClient, client, c.ClientConfig, namespaceNames) + for k, v := range customResources { + output.SaveResult(c.BundlePath, fmt.Sprintf("cluster-resources/custom-resources/%v", k), bytes.NewBuffer(v)) + } + output.SaveResult(c.BundlePath, "cluster-resources/custom-resources/custom-resources-errors.json", marshalErrors(crErrors)) + // imagepullsecrets imagePullSecrets, pullSecretsErrors := imagePullSecrets(ctx, client, namespaceNames) for k, v := range imagePullSecrets { @@ -502,6 +520,216 @@ func crdsV1beta(ctx context.Context, config *rest.Config) ([]byte, []string) { return b, nil } +func crs(ctx context.Context, dyn dynamic.Interface, client *kubernetes.Clientset, config *rest.Config, namespaces []string) (map[string][]byte, map[string]string) { + ok, err := discovery.HasResource(client, "apiextensions.k8s.io/v1", "CustomResourceDefinition") + if err != nil { + return nil, map[string]string{"discover apiextensions.k8s.io/v1": err.Error()} + } + if ok { + return crsV1(ctx, dyn, config, namespaces) + } + + return crsV1beta(ctx, dyn, config, namespaces) +} + +func crsV1(ctx context.Context, client dynamic.Interface, config *rest.Config, namespaces []string) (map[string][]byte, map[string]string) { + customResources := make(map[string][]byte) + errorList := make(map[string]string) + + crdClient, err := apiextensionsv1clientset.NewForConfig(config) + if err != nil { + errorList["crdClient"] = err.Error() + return customResources, errorList + } + + crds, err := crdClient.CustomResourceDefinitions().List(ctx, metav1.ListOptions{}) + if err != nil { + errorList["crdList"] = err.Error() + return customResources, errorList + } + + metaAccessor := meta.NewAccessor() + + // Loop through CRDs to fetch the CRs + for _, crd := range crds.Items { + // A resource that contains '/' is a subresource type and it has no + // object instances + if strings.ContainsAny(crd.Name, "/") { + continue + } + + var version string + if len(crd.Spec.Versions) > 0 { + version = crd.Spec.Versions[0].Name + } + gvr := schema.GroupVersionResource{ + Group: crd.Spec.Group, + Version: version, + Resource: crd.Spec.Names.Plural, + } + isNamespacedResource := crd.Spec.Scope == apiextensionsv1.NamespaceScoped + + // Fetch all resources of given type + customResourceList, err := client.Resource(gvr).List(ctx, metav1.ListOptions{}) + if err != nil { + errorList[crd.Name] = err.Error() + continue + } + + if len(customResourceList.Items) == 0 { + continue + } + + if !isNamespacedResource { + objects := []map[string]interface{}{} + for _, item := range customResourceList.Items { + objects = append(objects, item.Object) + } + b, err := yaml.Marshal(objects) + if err != nil { + errorList[crd.Name] = err.Error() + continue + } + customResources[fmt.Sprintf("%s.yaml", crd.Name)] = b + } else { + // Group fetched resources by the namespace + perNamespace := map[string][]map[string]interface{}{} + errors := []string{} + + for _, item := range customResourceList.Items { + ns, err := metaAccessor.Namespace(&item) + if err != nil { + errors = append(errors, err.Error()) + continue + } + if perNamespace[ns] == nil { + perNamespace[ns] = []map[string]interface{}{} + } + perNamespace[ns] = append(perNamespace[ns], item.Object) + } + + if len(errors) > 0 { + errorList[crd.Name] = strings.Join(errors, "\n") + } + + // Only include resources from requested namespaces + for _, ns := range namespaces { + if len(perNamespace[ns]) == 0 { + continue + } + + namespacedName := fmt.Sprintf("%s/%s", crd.Name, ns) + b, err := yaml.Marshal(perNamespace[ns]) + if err != nil { + errorList[namespacedName] = err.Error() + continue + } + + customResources[fmt.Sprintf("%s.yaml", namespacedName)] = b + } + } + } + + return customResources, errorList +} + +func crsV1beta(ctx context.Context, client dynamic.Interface, config *rest.Config, namespaces []string) (map[string][]byte, map[string]string) { + customResources := make(map[string][]byte) + errorList := make(map[string]string) + + crdClient, err := apiextensionsv1beta1clientset.NewForConfig(config) + if err != nil { + errorList["crdClient"] = err.Error() + return customResources, errorList + } + + crds, err := crdClient.CustomResourceDefinitions().List(ctx, metav1.ListOptions{}) + if err != nil { + errorList["crdList"] = err.Error() + return customResources, errorList + } + + metaAccessor := meta.NewAccessor() + + // Loop through CRDs to fetch the CRs + for _, crd := range crds.Items { + // A resource that contains '/' is a subresource type and it has no + // object instances + if strings.ContainsAny(crd.Name, "/") { + continue + } + + gvr := schema.GroupVersionResource{ + Group: crd.Spec.Group, + Version: crd.Spec.Version, + Resource: crd.Spec.Names.Plural, + } + isNamespacedResource := crd.Spec.Scope == apiextensionsv1beta1.NamespaceScoped + + // Fetch all resources of given type + customResourceList, err := client.Resource(gvr).List(ctx, metav1.ListOptions{}) + if err != nil { + errorList[crd.Name] = err.Error() + continue + } + + if len(customResourceList.Items) == 0 { + continue + } + + if !isNamespacedResource { + objects := []map[string]interface{}{} + for _, item := range customResourceList.Items { + objects = append(objects, item.Object) + } + b, err := yaml.Marshal(customResourceList.Items) + if err != nil { + errorList[crd.Name] = err.Error() + continue + } + customResources[fmt.Sprintf("%s.yaml", crd.Name)] = b + } else { + // Group fetched resources by the namespace + perNamespace := map[string][]map[string]interface{}{} + errors := []string{} + + for _, item := range customResourceList.Items { + ns, err := metaAccessor.Namespace(&item) + if err != nil { + errors = append(errors, err.Error()) + continue + } + if perNamespace[ns] == nil { + perNamespace[ns] = []map[string]interface{}{} + } + perNamespace[ns] = append(perNamespace[ns], item.Object) + } + + if len(errors) > 0 { + errorList[crd.Name] = strings.Join(errors, "\n") + } + + // Only include resources from requested namespaces + for _, ns := range namespaces { + if len(perNamespace[ns]) == 0 { + continue + } + + namespacedName := fmt.Sprintf("%s/%s", crd.Name, ns) + b, err := yaml.Marshal(perNamespace[ns]) + if err != nil { + errorList[namespacedName] = err.Error() + continue + } + + customResources[fmt.Sprintf("%s.yaml", namespacedName)] = b + } + } + } + + return customResources, errorList +} + func imagePullSecrets(ctx context.Context, client *kubernetes.Clientset, namespaces []string) (map[string][]byte, map[string]string) { imagePullSecrets := make(map[string][]byte) errors := make(map[string]string) diff --git a/pkg/redact/literal.go b/pkg/redact/literal.go index 2f9cfc97..f507e9ed 100644 --- a/pkg/redact/literal.go +++ b/pkg/redact/literal.go @@ -22,7 +22,7 @@ func literalString(matchString, path, name string) Redactor { } } -func (r literalRedactor) Redact(input io.Reader) io.Reader { +func (r literalRedactor) Redact(input io.Reader, path string) io.Reader { out, writer := io.Pipe() go func() { diff --git a/pkg/redact/multi_line.go b/pkg/redact/multi_line.go index d8b3e128..dfdbb4f5 100644 --- a/pkg/redact/multi_line.go +++ b/pkg/redact/multi_line.go @@ -28,7 +28,7 @@ func NewMultiLineRedactor(re1, re2, maskText, path, name string, isDefault bool) return &MultiLineRedactor{re1: compiled1, re2: compiled2, maskText: maskText, filePath: path, redactName: name, isDefault: isDefault}, nil } -func (r *MultiLineRedactor) Redact(input io.Reader) io.Reader { +func (r *MultiLineRedactor) Redact(input io.Reader, path string) io.Reader { out, writer := io.Pipe() go func() { var err error diff --git a/pkg/redact/redact.go b/pkg/redact/redact.go index eb4b5258..651f17cd 100644 --- a/pkg/redact/redact.go +++ b/pkg/redact/redact.go @@ -28,7 +28,7 @@ func init() { } type Redactor interface { - Redact(input io.Reader) io.Reader + Redact(input io.Reader, path string) io.Reader } // Redactions are indexed both by the file affected and by the name of the redactor @@ -59,7 +59,7 @@ func Redact(input io.Reader, path string, additionalRedactors []*troubleshootv1b nextReader := input for _, r := range redactors { - nextReader = r.Redact(nextReader) + nextReader = r.Redact(nextReader, path) } return nextReader, nil @@ -314,6 +314,40 @@ func getRedactors(path string) ([]Redactor, error) { redactors = append(redactors, r) } + customResources := []struct { + resource string + yamlPath string + }{ + { + resource: "installers.cluster.kurl.sh", + yamlPath: "*.spec.kubernetes.bootstrapToken", + }, + { + resource: "installers.cluster.kurl.sh", + yamlPath: "*.spec.kubernetes.certKey", + }, + { + resource: "installers.cluster.kurl.sh", + yamlPath: "*.spec.kubernetes.kubeadmToken", + }, + } + + uniqueCRs := map[string]bool{} + for _, cr := range customResources { + fileglob := fmt.Sprintf("cluster-resources/custom-resources/%s/*", cr.resource) + redactors = append(redactors, NewYamlRedactor(cr.yamlPath, fileglob, "")) + + // redact kubectl last applied annotation once for each resource since it contains copies of + // redacted fields + if !uniqueCRs[cr.resource] { + uniqueCRs[cr.resource] = true + redactors = append(redactors, &YamlRedactor{ + filePath: fileglob, + maskPath: []string{"*", "metadata", "annotations", "kubectl.kubernetes.io/last-applied-configuration"}, + }) + } + } + return redactors, nil } diff --git a/pkg/redact/redact_test.go b/pkg/redact/redact_test.go index 3d774ae8..d956284e 100644 --- a/pkg/redact/redact_test.go +++ b/pkg/redact/redact_test.go @@ -1637,7 +1637,7 @@ func Test_Redactors(t *testing.T) { nextReader := io.Reader(strings.NewReader(original)) for _, r := range redactors { - nextReader = r.Redact(nextReader) + nextReader = r.Redact(nextReader, "") } redacted, err := ioutil.ReadAll(nextReader) diff --git a/pkg/redact/single_line.go b/pkg/redact/single_line.go index a4306940..58ab2bae 100644 --- a/pkg/redact/single_line.go +++ b/pkg/redact/single_line.go @@ -23,7 +23,7 @@ func NewSingleLineRedactor(re, maskText, path, name string, isDefault bool) (*Si return &SingleLineRedactor{re: compiled, maskText: maskText, filePath: path, redactName: name, isDefault: isDefault}, nil } -func (r *SingleLineRedactor) Redact(input io.Reader) io.Reader { +func (r *SingleLineRedactor) Redact(input io.Reader, path string) io.Reader { out, writer := io.Pipe() go func() { diff --git a/pkg/redact/single_line_test.go b/pkg/redact/single_line_test.go index 46285db3..96c98a3a 100644 --- a/pkg/redact/single_line_test.go +++ b/pkg/redact/single_line_test.go @@ -104,10 +104,11 @@ func TestNewSingleLineRedactor(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req := require.New(t) + ResetRedactionList() reRunner, err := NewSingleLineRedactor(tt.re, MASK_TEXT, "testfile", tt.name, false) req.NoError(err) - outReader := reRunner.Redact(bytes.NewReader([]byte(tt.inputString))) + outReader := reRunner.Redact(bytes.NewReader([]byte(tt.inputString)), "") gotBytes, err := ioutil.ReadAll(outReader) req.NoError(err) req.Equal(tt.wantString, string(gotBytes)) diff --git a/pkg/redact/yaml.go b/pkg/redact/yaml.go index 8167de7e..701a21eb 100644 --- a/pkg/redact/yaml.go +++ b/pkg/redact/yaml.go @@ -5,9 +5,11 @@ import ( "bytes" "io" "io/ioutil" + "path/filepath" "strconv" "strings" + "github.com/replicatedhq/troubleshoot/pkg/logger" "gopkg.in/yaml.v2" ) @@ -24,7 +26,17 @@ func NewYamlRedactor(yamlPath, filePath, name string) *YamlRedactor { return &YamlRedactor{maskPath: pathComponents, filePath: filePath, redactName: name} } -func (r *YamlRedactor) Redact(input io.Reader) io.Reader { +func (r *YamlRedactor) Redact(input io.Reader, path string) io.Reader { + if r.filePath != "" { + match, err := filepath.Match(r.filePath, path) + if err != nil { + logger.Printf("Failed to match %q and %q: %v", r.filePath, path, err) + return input + } + if !match { + return input + } + } reader, writer := io.Pipe() go func() { var err error diff --git a/pkg/redact/yaml_test.go b/pkg/redact/yaml_test.go index 5d4601ec..2ec32f6b 100644 --- a/pkg/redact/yaml_test.go +++ b/pkg/redact/yaml_test.go @@ -294,7 +294,7 @@ xyz: req := require.New(t) yamlRunner := NewYamlRedactor(strings.Join(tt.path, "."), "testfile", tt.name) - outReader := yamlRunner.Redact(bytes.NewReader([]byte(tt.inputString))) + outReader := yamlRunner.Redact(bytes.NewReader([]byte(tt.inputString)), "testfile") gotBytes, err := ioutil.ReadAll(outReader) req.NoError(err) req.Equal(tt.wantString, string(gotBytes))