From 5079164ed033141a105d5f79def4587ae6cd534b Mon Sep 17 00:00:00 2001 From: Javier <reivaj0705@gmail.com> Date: Thu, 7 Jul 2022 16:25:45 -0500 Subject: [PATCH 1/3] Add tenant events command --- kubectl-minio/cmd/tenant-events.go | 156 +++++++++++++++++++++++++++++ kubectl-minio/cmd/tenant.go | 1 + 2 files changed, 157 insertions(+) create mode 100644 kubectl-minio/cmd/tenant-events.go diff --git a/kubectl-minio/cmd/tenant-events.go b/kubectl-minio/cmd/tenant-events.go new file mode 100644 index 00000000000..a74c8616683 --- /dev/null +++ b/kubectl-minio/cmd/tenant-events.go @@ -0,0 +1,156 @@ +// This file is part of MinIO Operator +// Copyright (C) 2022, MinIO, Inc. +// +// This code is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License, version 3, +// as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License, version 3, +// along with this program. If not, see <http://www.gnu.org/licenses/> + +package cmd + +import ( + "context" + "encoding/json" + "fmt" + "io" + "strings" + "text/tabwriter" + "time" + + "github.com/minio/kubectl-minio/cmd/helpers" + v2 "github.com/minio/operator/pkg/apis/minio.min.io/v2" + "github.com/spf13/cobra" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/duration" + "k8s.io/klog/v2" + "sigs.k8s.io/yaml" +) + +const ( + eventsDesc = `'events' command displays tenant events` +) + +type eventsCmd struct { + out io.Writer + errOut io.Writer + ns string + yamlOutput bool + jsonOutput bool +} + +func newTenantEventsCmd(out io.Writer, errOut io.Writer) *cobra.Command { + c := &eventsCmd{out: out, errOut: errOut} + + cmd := &cobra.Command{ + Use: "events <TENANTNAME>", + Short: "Display tenant events", + Long: eventsDesc, + Example: ` kubectl minio events tenant1`, + Args: func(cmd *cobra.Command, args []string) error { + return c.validate(args) + }, + RunE: func(cmd *cobra.Command, args []string) error { + err := c.run(args) + if err != nil { + klog.Warning(err) + return err + } + return nil + }, + } + cmd = helpers.DisableHelp(cmd) + f := cmd.Flags() + f.StringVarP(&c.ns, "namespace", "n", "", "namespace scope for this request") + f.BoolVarP(&c.yamlOutput, "yaml", "y", false, "yaml output") + f.BoolVarP(&c.jsonOutput, "json", "j", false, "json output") + return cmd +} + +func (d *eventsCmd) validate(args []string) error { + return validateTenantArgs("events", args) +} + +func (d *eventsCmd) run(args []string) error { + oclient, err := helpers.GetKubeOperatorClient() + if err != nil { + return err + } + if d.ns == "" || d.ns == helpers.DefaultNamespace { + d.ns, err = getTenantNamespace(oclient, args[0]) + if err != nil { + return err + } + } + tenant, err := oclient.MinioV2().Tenants(d.ns).Get(context.Background(), args[0], metav1.GetOptions{}) + if err != nil { + return err + } + return d.printTenantEvents(tenant) +} + +func (d *eventsCmd) printTenantEvents(tenant *v2.Tenant) error { + events, err := d.getTenantEvents(tenant) + if err != nil { + return err + } + if !d.jsonOutput && !d.yamlOutput { + d.printRawTenantEvents(events) + return nil + } + if d.jsonOutput && d.yamlOutput { + return fmt.Errorf("Only one output can be used to display events") + } + if d.jsonOutput { + return d.printJSONTenantEvents(events) + } + if d.yamlOutput { + return d.printYAMLTenantEvents(events) + } + return nil +} + +func (d *eventsCmd) getTenantEvents(tenant *v2.Tenant) (*v1.EventList, error) { + path, _ := rootCmd.Flags().GetString(kubeconfig) + kubeClient, err := helpers.GetKubeClient(path) + if err != nil { + return nil, err + } + return kubeClient.CoreV1().Events(tenant.Namespace).List(context.Background(), metav1.ListOptions{}) +} + +func (d *eventsCmd) printRawTenantEvents(events *v1.EventList) { + var s strings.Builder + w := new(tabwriter.Writer) + w.Init(d.out, 8, 8, 0, '\t', 0) + defer w.Flush() + s.WriteString(fmt.Sprintf("%s\t%s\t%s\t%s\t%s", "LAST SEEN", "TYPE", "REASON", "OBJECT", "MESSAGE")) + for _, event := range events.Items { + s.WriteString( + fmt.Sprintf("\n%s\t%s\t%s\t%s\t%s", + duration.HumanDuration(time.Since(event.LastTimestamp.Time)), event.Type, event.Reason, event.InvolvedObject.Name, event.Message)) + } + fmt.Fprintln(w, s.String()) +} + +func (d *eventsCmd) printJSONTenantEvents(events *v1.EventList) error { + enc := json.NewEncoder(d.out) + enc.SetIndent("", " ") + return enc.Encode(events) +} + +func (d *eventsCmd) printYAMLTenantEvents(events *v1.EventList) error { + eventsYAML, err := yaml.Marshal(events) + if err != nil { + return err + } + fmt.Fprintln(d.out, string(eventsYAML)) + return nil +} diff --git a/kubectl-minio/cmd/tenant.go b/kubectl-minio/cmd/tenant.go index e2a022d61e8..3bf91820ccd 100644 --- a/kubectl-minio/cmd/tenant.go +++ b/kubectl-minio/cmd/tenant.go @@ -80,6 +80,7 @@ func newTenantCmd(out io.Writer, errOut io.Writer) *cobra.Command { cmd.AddCommand(newTenantDeleteCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) cmd.AddCommand(newTenantReportCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) cmd.AddCommand(newTenantStatusCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) + cmd.AddCommand(newTenantEventsCmd(cmd.OutOrStdout(), cmd.ErrOrStderr())) return cmd } From beae928b2418d16060d6cd34332710f03a98f6a8 Mon Sep 17 00:00:00 2001 From: Javier <reivaj0705@gmail.com> Date: Thu, 7 Jul 2022 16:29:27 -0500 Subject: [PATCH 2/3] One liner --- kubectl-minio/cmd/tenant-events.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kubectl-minio/cmd/tenant-events.go b/kubectl-minio/cmd/tenant-events.go index a74c8616683..5da547eb222 100644 --- a/kubectl-minio/cmd/tenant-events.go +++ b/kubectl-minio/cmd/tenant-events.go @@ -133,9 +133,7 @@ func (d *eventsCmd) printRawTenantEvents(events *v1.EventList) { defer w.Flush() s.WriteString(fmt.Sprintf("%s\t%s\t%s\t%s\t%s", "LAST SEEN", "TYPE", "REASON", "OBJECT", "MESSAGE")) for _, event := range events.Items { - s.WriteString( - fmt.Sprintf("\n%s\t%s\t%s\t%s\t%s", - duration.HumanDuration(time.Since(event.LastTimestamp.Time)), event.Type, event.Reason, event.InvolvedObject.Name, event.Message)) + s.WriteString(fmt.Sprintf("\n%s\t%s\t%s\t%s\t%s", duration.HumanDuration(time.Since(event.LastTimestamp.Time)), event.Type, event.Reason, event.InvolvedObject.Name, event.Message)) } fmt.Fprintln(w, s.String()) } From 78146aac51335831f505652093b7fa212a48fb88 Mon Sep 17 00:00:00 2001 From: Javier <reivaj0705@gmail.com> Date: Fri, 8 Jul 2022 12:38:31 -0500 Subject: [PATCH 3/3] Address comments --- kubectl-minio/cmd/tenant-events.go | 42 +++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/kubectl-minio/cmd/tenant-events.go b/kubectl-minio/cmd/tenant-events.go index 5da547eb222..eb68eb4a824 100644 --- a/kubectl-minio/cmd/tenant-events.go +++ b/kubectl-minio/cmd/tenant-events.go @@ -20,12 +20,11 @@ import ( "encoding/json" "fmt" "io" - "strings" - "text/tabwriter" "time" "github.com/minio/kubectl-minio/cmd/helpers" v2 "github.com/minio/operator/pkg/apis/minio.min.io/v2" + "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -127,15 +126,40 @@ func (d *eventsCmd) getTenantEvents(tenant *v2.Tenant) (*v1.EventList, error) { } func (d *eventsCmd) printRawTenantEvents(events *v1.EventList) { - var s strings.Builder - w := new(tabwriter.Writer) - w.Init(d.out, 8, 8, 0, '\t', 0) - defer w.Flush() - s.WriteString(fmt.Sprintf("%s\t%s\t%s\t%s\t%s", "LAST SEEN", "TYPE", "REASON", "OBJECT", "MESSAGE")) + table := d.initEventsTable() + data := d.getEventsData(events) + table.AppendBulk(data) + table.Render() +} + +func (d *eventsCmd) initEventsTable() *tablewriter.Table { + table := tablewriter.NewWriter(d.out) + table.SetAutoWrapText(false) + table.SetAutoFormatHeaders(true) + table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) + table.SetAlignment(tablewriter.ALIGN_LEFT) + table.SetCenterSeparator("") + table.SetColumnSeparator("") + table.SetRowSeparator("") + table.SetHeaderLine(false) + table.SetBorder(false) + table.SetTablePadding("\t") + table.SetNoWhiteSpace(true) + table.SetHeader([]string{"LAST SEEN", "TYPE", "REASON", "OBJECT", "MESSAGE"}) + return table +} + +func (d *eventsCmd) getEventsData(events *v1.EventList) (data [][]string) { for _, event := range events.Items { - s.WriteString(fmt.Sprintf("\n%s\t%s\t%s\t%s\t%s", duration.HumanDuration(time.Since(event.LastTimestamp.Time)), event.Type, event.Reason, event.InvolvedObject.Name, event.Message)) + data = append(data, []string{ + duration.HumanDuration(time.Since(event.LastTimestamp.Time)), + event.Type, + event.Reason, + event.InvolvedObject.Name, + event.Message, + }) } - fmt.Fprintln(w, s.String()) + return data } func (d *eventsCmd) printJSONTenantEvents(events *v1.EventList) error {