From 9eee59bb22cc3975cf0a2f6b99fe405d65480793 Mon Sep 17 00:00:00 2001 From: Steven Powell Date: Tue, 2 Feb 2021 16:43:57 -0700 Subject: [PATCH 01/10] Added audit logs to minikube logs output --- pkg/minikube/audit/audit.go | 31 ++++++++++++++++++++++++++ pkg/minikube/audit/entry.go | 42 ++++++++++++++++++++++++++++++++++- pkg/minikube/audit/logFile.go | 4 ++-- pkg/minikube/logs/logs.go | 16 +++++++++++++ 4 files changed, 90 insertions(+), 3 deletions(-) diff --git a/pkg/minikube/audit/audit.go b/pkg/minikube/audit/audit.go index 83b0ee98c2..4c3f22b41f 100644 --- a/pkg/minikube/audit/audit.go +++ b/pkg/minikube/audit/audit.go @@ -17,6 +17,8 @@ limitations under the License. package audit import ( + "bufio" + "fmt" "os" "os/user" "strings" @@ -76,3 +78,32 @@ func shouldLog() bool { } return true } + +// Retrieve the last n lines from the log. +func Retrieve(n int) (string, error) { + if n <= 0 { + return "", fmt.Errorf("number of lines to retrieve must be 1 or greater") + } + if currentLogFile == nil { + if err := setLogFile(); err != nil { + return "", fmt.Errorf("failed to set the log file: %v", err) + } + } + var l []string + s := bufio.NewScanner(currentLogFile) + for s.Scan() { + // pop off the earliest line if already at desired log length + if len(l) == n { + l = l[1:] + } + l = append(l, s.Text()) + } + if err := s.Err(); err != nil { + return "", fmt.Errorf("failed to read from audit file: %v", err) + } + t, err := linesToTable(l) + if err != nil { + return "", fmt.Errorf("failed to convert lines to table: %v", err) + } + return t, nil +} diff --git a/pkg/minikube/audit/entry.go b/pkg/minikube/audit/entry.go index 75b32af9a0..08c2773c14 100644 --- a/pkg/minikube/audit/entry.go +++ b/pkg/minikube/audit/entry.go @@ -17,8 +17,12 @@ limitations under the License. package audit import ( + "bytes" + "encoding/json" + "fmt" "time" + "github.com/olekukonko/tablewriter" "github.com/spf13/viper" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" @@ -26,7 +30,7 @@ import ( // entry represents the execution of a command. type entry struct { - data map[string]string + Data map[string]string `json:"data"` } // Type returns the cloud events compatible type of this struct. @@ -47,3 +51,39 @@ func newEntry(command string, args string, user string, startTime time.Time, end }, } } + +// toFields converts an entry to an array of fields. +func (e *entry) toFields() []string { + d := e.Data + return []string{d["command"], d["args"], d["profile"], d["user"], d["startTime"], d["endTime"]} +} + +// linesToFields converts audit lines into arrays of fields. +func linesToFields(lines []string) ([][]string, error) { + c := [][]string{} + for _, l := range lines { + e := &entry{} + if err := json.Unmarshal([]byte(l), e); err != nil { + return nil, fmt.Errorf("failed to unmarshal %q: %v", l, err) + } + c = append(c, e.toFields()) + } + return c, nil +} + +// linesToTable converts audit lines into a formatted table. +func linesToTable(lines []string) (string, error) { + f, err := linesToFields(lines) + if err != nil { + return "", fmt.Errorf("failed to convert lines to fields: %v", err) + } + b := new(bytes.Buffer) + t := tablewriter.NewWriter(b) + t.SetHeader([]string{"Command", "Args", "Profile", "User", "Start Time", "End Time"}) + t.SetAutoFormatHeaders(false) + t.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true}) + t.SetCenterSeparator("|") + t.AppendBulk(f) + t.Render() + return b.String(), nil +} diff --git a/pkg/minikube/audit/logFile.go b/pkg/minikube/audit/logFile.go index 1d24284412..79febd6927 100644 --- a/pkg/minikube/audit/logFile.go +++ b/pkg/minikube/audit/logFile.go @@ -30,7 +30,7 @@ var currentLogFile *os.File // setLogFile sets the logPath and creates the log file if it doesn't exist. func setLogFile() error { lp := localpath.AuditLog() - f, err := os.OpenFile(lp, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + f, err := os.OpenFile(lp, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644) if err != nil { return fmt.Errorf("unable to open %s: %v", lp, err) } @@ -45,7 +45,7 @@ func appendToLog(entry *entry) error { return err } } - e := register.CloudEvent(entry, entry.data) + e := register.CloudEvent(entry, entry.Data) bs, err := e.MarshalJSON() if err != nil { return fmt.Errorf("error marshalling event: %v", err) diff --git a/pkg/minikube/logs/logs.go b/pkg/minikube/logs/logs.go index 43b4c08e67..a9063308f2 100644 --- a/pkg/minikube/logs/logs.go +++ b/pkg/minikube/logs/logs.go @@ -29,6 +29,7 @@ import ( "github.com/pkg/errors" "k8s.io/klog/v2" + "k8s.io/minikube/pkg/minikube/audit" "k8s.io/minikube/pkg/minikube/bootstrapper" "k8s.io/minikube/pkg/minikube/command" "k8s.io/minikube/pkg/minikube/config" @@ -188,12 +189,27 @@ func Output(r cruntime.Manager, bs bootstrapper.Bootstrapper, cfg config.Cluster } } + outputAudit(lines, failed) + if len(failed) > 0 { return fmt.Errorf("unable to fetch logs for: %s", strings.Join(failed, ", ")) } return nil } +// outputAudit displays the audit logs. +func outputAudit(lines int, failed []string) { + out.Step(style.Empty, "") + out.Step(style.Empty, "==> Audit <==") + a, err := audit.Retrieve(lines) + if err != nil { + klog.Errorf("failed to get audit logs with error: %v", err) + failed = append(failed, "audit") + return + } + out.Step(style.Empty, a) +} + // logCommands returns a list of commands that would be run to receive the anticipated logs func logCommands(r cruntime.Manager, bs bootstrapper.Bootstrapper, cfg config.ClusterConfig, length int, follow bool) map[string]string { cmds := bs.LogCommands(cfg, bootstrapper.LogOptions{Lines: length, Follow: follow}) From 69ffef11a88aafe578dbde38eeb780f614af26ce Mon Sep 17 00:00:00 2001 From: Steven Powell Date: Thu, 4 Feb 2021 11:37:27 -0700 Subject: [PATCH 02/10] Added unit tests and refactored report making --- pkg/minikube/audit/audit.go | 31 ----------- pkg/minikube/audit/entry.go | 4 +- pkg/minikube/audit/entry_test.go | 88 +++++++++++++++++++++++++++++++ pkg/minikube/audit/report.go | 65 +++++++++++++++++++++++ pkg/minikube/audit/report_test.go | 62 ++++++++++++++++++++++ pkg/minikube/logs/logs.go | 20 ++++--- 6 files changed, 230 insertions(+), 40 deletions(-) create mode 100644 pkg/minikube/audit/entry_test.go create mode 100644 pkg/minikube/audit/report.go create mode 100644 pkg/minikube/audit/report_test.go diff --git a/pkg/minikube/audit/audit.go b/pkg/minikube/audit/audit.go index 4c3f22b41f..83b0ee98c2 100644 --- a/pkg/minikube/audit/audit.go +++ b/pkg/minikube/audit/audit.go @@ -17,8 +17,6 @@ limitations under the License. package audit import ( - "bufio" - "fmt" "os" "os/user" "strings" @@ -78,32 +76,3 @@ func shouldLog() bool { } return true } - -// Retrieve the last n lines from the log. -func Retrieve(n int) (string, error) { - if n <= 0 { - return "", fmt.Errorf("number of lines to retrieve must be 1 or greater") - } - if currentLogFile == nil { - if err := setLogFile(); err != nil { - return "", fmt.Errorf("failed to set the log file: %v", err) - } - } - var l []string - s := bufio.NewScanner(currentLogFile) - for s.Scan() { - // pop off the earliest line if already at desired log length - if len(l) == n { - l = l[1:] - } - l = append(l, s.Text()) - } - if err := s.Err(); err != nil { - return "", fmt.Errorf("failed to read from audit file: %v", err) - } - t, err := linesToTable(l) - if err != nil { - return "", fmt.Errorf("failed to convert lines to table: %v", err) - } - return t, nil -} diff --git a/pkg/minikube/audit/entry.go b/pkg/minikube/audit/entry.go index 08c2773c14..1d14ded9a4 100644 --- a/pkg/minikube/audit/entry.go +++ b/pkg/minikube/audit/entry.go @@ -72,14 +72,14 @@ func linesToFields(lines []string) ([][]string, error) { } // linesToTable converts audit lines into a formatted table. -func linesToTable(lines []string) (string, error) { +func linesToTable(lines []string, headers []string) (string, error) { f, err := linesToFields(lines) if err != nil { return "", fmt.Errorf("failed to convert lines to fields: %v", err) } b := new(bytes.Buffer) t := tablewriter.NewWriter(b) - t.SetHeader([]string{"Command", "Args", "Profile", "User", "Start Time", "End Time"}) + t.SetHeader(headers) t.SetAutoFormatHeaders(false) t.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true}) t.SetCenterSeparator("|") diff --git a/pkg/minikube/audit/entry_test.go b/pkg/minikube/audit/entry_test.go new file mode 100644 index 0000000000..1d7867014a --- /dev/null +++ b/pkg/minikube/audit/entry_test.go @@ -0,0 +1,88 @@ +/* +Copyright 2020 The Kubernetes Authors All rights reserved. + +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 audit + +import ( + "strings" + "testing" + "time" + + "github.com/spf13/viper" + "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/constants" +) + +func TestEntry(t *testing.T) { + c := "start" + a := "--alsologtostderr" + p := "profile1" + u := "user1" + st := time.Now() + stFormatted := st.Format(constants.TimeFormat) + et := time.Now() + etFormatted := et.Format(constants.TimeFormat) + + // save current profile in case something depends on it + oldProfile := viper.GetString(config.ProfileName) + viper.Set(config.ProfileName, p) + e := newEntry(c, a, u, st, et) + viper.Set(config.ProfileName, oldProfile) + + t.Run("TestNewEntry", func(t *testing.T) { + d := e.Data + + tests := []struct { + key string + want string + }{ + {"command", c}, + {"args", a}, + {"profile", p}, + {"user", u}, + {"startTime", stFormatted}, + {"endTime", etFormatted}, + } + + for _, tt := range tests { + got := d[tt.key] + + if got != tt.want { + t.Errorf("Data[%q] = %s; want %s", tt.key, got, tt.want) + } + } + }) + + t.Run("TestType", func(t *testing.T) { + got := e.Type() + want := "io.k8s.sigs.minikube.audit" + + if got != want { + t.Errorf("Type() = %s; want %s", got, want) + } + }) + + t.Run("TestToField", func(t *testing.T) { + got := e.toFields() + gotString := strings.Join(got, ",") + want := []string{c, a, p, u, stFormatted, etFormatted} + wantString := strings.Join(want, ",") + + if gotString != wantString { + t.Errorf("toFields() = %s; want %s", gotString, wantString) + } + }) +} diff --git a/pkg/minikube/audit/report.go b/pkg/minikube/audit/report.go new file mode 100644 index 0000000000..1ef770f12d --- /dev/null +++ b/pkg/minikube/audit/report.go @@ -0,0 +1,65 @@ +/* +Copyright 2020 The Kubernetes Authors All rights reserved. + +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 audit + +import ( + "bufio" + "fmt" +) + +type Data struct { + headers []string + logs []string +} + +// Report is created from the log file. +func Report(lines int) (*Data, error) { + if lines <= 0 { + return nil, fmt.Errorf("number of lines must be 1 or greater") + } + if currentLogFile == nil { + if err := setLogFile(); err != nil { + return nil, fmt.Errorf("failed to set the log file: %v", err) + } + } + var l []string + s := bufio.NewScanner(currentLogFile) + for s.Scan() { + // pop off the earliest line if already at desired log length + if len(l) == lines { + l = l[1:] + } + l = append(l, s.Text()) + } + if err := s.Err(); err != nil { + return nil, fmt.Errorf("failed to read from audit file: %v", err) + } + r := &Data{ + []string{"Command", "Args", "Profile", "User", "Start Time", "End Time"}, + l, + } + return r, nil +} + +// Table creates a formatted table using last n lines from the report. +func (r *Data) Table() (string, error) { + t, err := linesToTable(r.logs, r.headers) + if err != nil { + return "", fmt.Errorf("failed to convert lines to table: %v", err) + } + return t, nil +} diff --git a/pkg/minikube/audit/report_test.go b/pkg/minikube/audit/report_test.go new file mode 100644 index 0000000000..4dd727ecdd --- /dev/null +++ b/pkg/minikube/audit/report_test.go @@ -0,0 +1,62 @@ +/* +Copyright 2020 The Kubernetes Authors All rights reserved. + +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 audit + +import ( + "io/ioutil" + "os" + "testing" +) + +func TestAuditReport(t *testing.T) { + f, err := ioutil.TempFile("", "audit.json") + if err != nil { + t.Fatalf("failed creating temporary file: %v", err) + } + defer os.Remove(f.Name()) + + s := `{"data":{"args":"-p mini1","command":"start","endTime":"Wed, 03 Feb 2021 15:33:05 MST","profile":"mini1","startTime":"Wed, 03 Feb 2021 15:30:33 MST","user":"user1"},"datacontenttype":"application/json","id":"9b7593cb-fbec-49e5-a3ce-bdc2d0bfb208","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.si gs.minikube.audit"} +{"data":{"args":"-p mini1","command":"start","endTime":"Wed, 03 Feb 2021 15:33:05 MST","profile":"mini1","startTime":"Wed, 03 Feb 2021 15:30:33 MST","user":"user1"},"datacontenttype":"application/json","id":"9b7593cb-fbec-49e5-a3ce-bdc2d0bfb208","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.si gs.minikube.audit"} +{"data":{"args":"--user user2","command":"logs","endTime":"Tue, 02 Feb 2021 16:46:20 MST","profile":"minikube","startTime":"Tue, 02 Feb 2021 16:46:00 MST","user":"user2"},"datacontenttype":"application/json","id":"fec03227-2484-48b6-880a-88fd010b5efd","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.audit"}` + + if _, err := f.WriteString(s); err != nil { + t.Fatalf("failed writing to file: %v", err) + } + if _, err := f.Seek(0, 0); err != nil { + t.Fatalf("failed seeking to start of file: %v", err) + } + + oldLogFile := *currentLogFile + defer func() { currentLogFile = &oldLogFile }() + currentLogFile = f + + wantedLines := 2 + r, err := Report(wantedLines) + if err != nil { + t.Fatalf("failed to create report: %v", err) + } + + if len(r.logs) != wantedLines { + t.Fatalf("report has %d lines of logs, want %d", len(r.logs), wantedLines) + } + + t.Run("TestTable", func(t *testing.T) { + if _, err := r.Table(); err != nil { + t.Errorf("failed to create table from report: %v", err) + } + }) +} diff --git a/pkg/minikube/logs/logs.go b/pkg/minikube/logs/logs.go index a9063308f2..4fcac53b7e 100644 --- a/pkg/minikube/logs/logs.go +++ b/pkg/minikube/logs/logs.go @@ -189,7 +189,10 @@ func Output(r cruntime.Manager, bs bootstrapper.Bootstrapper, cfg config.Cluster } } - outputAudit(lines, failed) + if err := outputAudit(lines); err != nil { + klog.Error(err) + failed = append(failed, "audit") + } if len(failed) > 0 { return fmt.Errorf("unable to fetch logs for: %s", strings.Join(failed, ", ")) @@ -198,16 +201,19 @@ func Output(r cruntime.Manager, bs bootstrapper.Bootstrapper, cfg config.Cluster } // outputAudit displays the audit logs. -func outputAudit(lines int, failed []string) { +func outputAudit(lines int) error { out.Step(style.Empty, "") out.Step(style.Empty, "==> Audit <==") - a, err := audit.Retrieve(lines) + r, err := audit.Report(lines) if err != nil { - klog.Errorf("failed to get audit logs with error: %v", err) - failed = append(failed, "audit") - return + return fmt.Errorf("failed to create audit report with error: %v", err) } - out.Step(style.Empty, a) + t, err := r.Table() + if err != nil { + return fmt.Errorf("failed to create audit table with error: %v", err) + } + out.Step(style.Empty, t) + return nil } // logCommands returns a list of commands that would be run to receive the anticipated logs From 210accb328f7c3105a9d27cd104511a62b1f3dd3 Mon Sep 17 00:00:00 2001 From: Steven Powell Date: Thu, 4 Feb 2021 12:56:10 -0700 Subject: [PATCH 03/10] renamed lines to logs --- pkg/minikube/audit/entry.go | 14 +++++++------- pkg/minikube/audit/report.go | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/minikube/audit/entry.go b/pkg/minikube/audit/entry.go index 1d14ded9a4..7664a65d04 100644 --- a/pkg/minikube/audit/entry.go +++ b/pkg/minikube/audit/entry.go @@ -58,10 +58,10 @@ func (e *entry) toFields() []string { return []string{d["command"], d["args"], d["profile"], d["user"], d["startTime"], d["endTime"]} } -// linesToFields converts audit lines into arrays of fields. -func linesToFields(lines []string) ([][]string, error) { +// logsToFields converts audit logs into arrays of fields. +func logsToFields(logs []string) ([][]string, error) { c := [][]string{} - for _, l := range lines { + for _, l := range logs { e := &entry{} if err := json.Unmarshal([]byte(l), e); err != nil { return nil, fmt.Errorf("failed to unmarshal %q: %v", l, err) @@ -71,11 +71,11 @@ func linesToFields(lines []string) ([][]string, error) { return c, nil } -// linesToTable converts audit lines into a formatted table. -func linesToTable(lines []string, headers []string) (string, error) { - f, err := linesToFields(lines) +// logsToTable converts audit lines into a formatted table. +func logsToTable(logs []string, headers []string) (string, error) { + f, err := logsToFields(logs) if err != nil { - return "", fmt.Errorf("failed to convert lines to fields: %v", err) + return "", fmt.Errorf("failed to convert logs to fields: %v", err) } b := new(bytes.Buffer) t := tablewriter.NewWriter(b) diff --git a/pkg/minikube/audit/report.go b/pkg/minikube/audit/report.go index 1ef770f12d..5b48d6da1e 100644 --- a/pkg/minikube/audit/report.go +++ b/pkg/minikube/audit/report.go @@ -55,11 +55,11 @@ func Report(lines int) (*Data, error) { return r, nil } -// Table creates a formatted table using last n lines from the report. +// Table creates a formatted table using last n logs from the report. func (r *Data) Table() (string, error) { - t, err := linesToTable(r.logs, r.headers) + t, err := logsToTable(r.logs, r.headers) if err != nil { - return "", fmt.Errorf("failed to convert lines to table: %v", err) + return "", fmt.Errorf("failed to convert logs to table: %v", err) } return t, nil } From e0b9dce3bf0695a4f9227f0b52002c1039ac1792 Mon Sep 17 00:00:00 2001 From: Steven Powell Date: Wed, 10 Feb 2021 17:09:07 -0700 Subject: [PATCH 04/10] Added version to audit logs & logs output and changes entry structure --- pkg/minikube/audit/audit.go | 3 +- pkg/minikube/audit/entry.go | 90 ++++++++++++++++++++++++----------- pkg/minikube/audit/logFile.go | 4 +- pkg/minikube/audit/report.go | 22 +++++---- 4 files changed, 78 insertions(+), 41 deletions(-) diff --git a/pkg/minikube/audit/audit.go b/pkg/minikube/audit/audit.go index 83b0ee98c2..7b7e9d2d95 100644 --- a/pkg/minikube/audit/audit.go +++ b/pkg/minikube/audit/audit.go @@ -25,6 +25,7 @@ import ( "github.com/spf13/viper" "k8s.io/klog" "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/version" ) // userName pulls the user flag, if empty gets the os username. @@ -54,7 +55,7 @@ func Log(startTime time.Time) { if !shouldLog() { return } - e := newEntry(os.Args[1], args(), userName(), startTime, time.Now()) + e := newEntry(os.Args[1], args(), userName(), version.GetVersion(), startTime, time.Now()) if err := appendToLog(e); err != nil { klog.Error(err) } diff --git a/pkg/minikube/audit/entry.go b/pkg/minikube/audit/entry.go index 7664a65d04..33b44dfb3f 100644 --- a/pkg/minikube/audit/entry.go +++ b/pkg/minikube/audit/entry.go @@ -24,66 +24,98 @@ import ( "github.com/olekukonko/tablewriter" "github.com/spf13/viper" + "k8s.io/klog" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" ) -// entry represents the execution of a command. -type entry struct { - Data map[string]string `json:"data"` +// singleEntry is the log entry of a single command. +type singleEntry struct { + args string + command string + endTime string + profile string + startTime string + user string + version string + Data map[string]string `json:"data"` } // Type returns the cloud events compatible type of this struct. -func (e *entry) Type() string { +func (e *singleEntry) Type() string { return "io.k8s.sigs.minikube.audit" } +// assignFields converts the map values to their proper fields +func (e *singleEntry) assignFields() { + e.args = e.Data["args"] + e.command = e.Data["command"] + e.endTime = e.Data["endTime"] + e.profile = e.Data["profile"] + e.startTime = e.Data["startTime"] + e.user = e.Data["user"] + e.version = e.Data["version"] +} + +// toMap combines fields into a string map +func (e *singleEntry) toMap() map[string]string { + return map[string]string{ + "args": e.args, + "command": e.command, + "endTime": e.endTime, + "profile": e.profile, + "startTime": e.startTime, + "user": e.user, + "version": e.version, + } +} + // newEntry returns a new audit type. -func newEntry(command string, args string, user string, startTime time.Time, endTime time.Time) *entry { - return &entry{ - map[string]string{ - "args": args, - "command": command, - "endTime": endTime.Format(constants.TimeFormat), - "profile": viper.GetString(config.ProfileName), - "startTime": startTime.Format(constants.TimeFormat), - "user": user, - }, +func newEntry(command string, args string, user string, version string, startTime time.Time, endTime time.Time) *singleEntry { + return &singleEntry{ + args: args, + command: command, + endTime: endTime.Format(constants.TimeFormat), + profile: viper.GetString(config.ProfileName), + startTime: startTime.Format(constants.TimeFormat), + user: user, + version: version, } } // toFields converts an entry to an array of fields. -func (e *entry) toFields() []string { - d := e.Data - return []string{d["command"], d["args"], d["profile"], d["user"], d["startTime"], d["endTime"]} +func (e *singleEntry) toFields() []string { + return []string{e.command, e.args, e.profile, e.user, e.version, e.startTime, e.endTime} } -// logsToFields converts audit logs into arrays of fields. -func logsToFields(logs []string) ([][]string, error) { - c := [][]string{} +// logsToEntries converts audit logs into arrays of entries. +func logsToEntries(logs []string) ([]singleEntry, error) { + c := []singleEntry{} for _, l := range logs { - e := &entry{} - if err := json.Unmarshal([]byte(l), e); err != nil { + e := singleEntry{} + if err := json.Unmarshal([]byte(l), &e); err != nil { return nil, fmt.Errorf("failed to unmarshal %q: %v", l, err) } - c = append(c, e.toFields()) + e.assignFields() + c = append(c, e) } return c, nil } -// logsToTable converts audit lines into a formatted table. -func logsToTable(logs []string, headers []string) (string, error) { - f, err := logsToFields(logs) - if err != nil { - return "", fmt.Errorf("failed to convert logs to fields: %v", err) +// entriesToTable converts audit lines into a formatted table. +func entriesToTable(entries []singleEntry, headers []string) (string, error) { + c := [][]string{} + for _, e := range entries { + c = append(c, e.toFields()) } + klog.Info(c) b := new(bytes.Buffer) t := tablewriter.NewWriter(b) t.SetHeader(headers) t.SetAutoFormatHeaders(false) t.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true}) t.SetCenterSeparator("|") - t.AppendBulk(f) + t.AppendBulk(c) t.Render() return b.String(), nil } diff --git a/pkg/minikube/audit/logFile.go b/pkg/minikube/audit/logFile.go index 79febd6927..af6e2741a9 100644 --- a/pkg/minikube/audit/logFile.go +++ b/pkg/minikube/audit/logFile.go @@ -39,13 +39,13 @@ func setLogFile() error { } // appendToLog appends the audit entry to the log file. -func appendToLog(entry *entry) error { +func appendToLog(entry *singleEntry) error { if currentLogFile == nil { if err := setLogFile(); err != nil { return err } } - e := register.CloudEvent(entry, entry.Data) + e := register.CloudEvent(entry, entry.toMap()) bs, err := e.MarshalJSON() if err != nil { return fmt.Errorf("error marshalling event: %v", err) diff --git a/pkg/minikube/audit/report.go b/pkg/minikube/audit/report.go index 5b48d6da1e..3b316e86dd 100644 --- a/pkg/minikube/audit/report.go +++ b/pkg/minikube/audit/report.go @@ -23,7 +23,7 @@ import ( type Data struct { headers []string - logs []string + entries []singleEntry } // Report is created from the log file. @@ -36,28 +36,32 @@ func Report(lines int) (*Data, error) { return nil, fmt.Errorf("failed to set the log file: %v", err) } } - var l []string + var logs []string s := bufio.NewScanner(currentLogFile) for s.Scan() { // pop off the earliest line if already at desired log length - if len(l) == lines { - l = l[1:] + if len(logs) == lines { + logs = logs[1:] } - l = append(l, s.Text()) + logs = append(logs, s.Text()) } if err := s.Err(); err != nil { return nil, fmt.Errorf("failed to read from audit file: %v", err) } + e, err := logsToEntries(logs) + if err != nil { + return nil, fmt.Errorf("failed to convert logs to entries: %v", err) + } r := &Data{ - []string{"Command", "Args", "Profile", "User", "Start Time", "End Time"}, - l, + []string{"Command", "Args", "Profile", "User", "Version", "Start Time", "End Time"}, + e, } return r, nil } -// Table creates a formatted table using last n logs from the report. +// Table creates a formatted table using entries from the report. func (r *Data) Table() (string, error) { - t, err := logsToTable(r.logs, r.headers) + t, err := entriesToTable(r.entries, r.headers) if err != nil { return "", fmt.Errorf("failed to convert logs to table: %v", err) } From dbafdd984e384b197edfe4505ef493bb40d6a6d9 Mon Sep 17 00:00:00 2001 From: Steven Powell Date: Fri, 12 Feb 2021 12:56:15 -0700 Subject: [PATCH 05/10] Made profile optional param and added more unit tests --- pkg/minikube/audit/entry.go | 12 ++-- pkg/minikube/audit/entry_test.go | 91 +++++++++++++++++++++++------- pkg/minikube/audit/logFile_test.go | 2 +- pkg/minikube/audit/report.go | 8 +-- pkg/minikube/audit/report_test.go | 10 +--- pkg/minikube/logs/logs.go | 6 +- 6 files changed, 85 insertions(+), 44 deletions(-) diff --git a/pkg/minikube/audit/entry.go b/pkg/minikube/audit/entry.go index 33b44dfb3f..58503be55c 100644 --- a/pkg/minikube/audit/entry.go +++ b/pkg/minikube/audit/entry.go @@ -71,12 +71,16 @@ func (e *singleEntry) toMap() map[string]string { } // newEntry returns a new audit type. -func newEntry(command string, args string, user string, version string, startTime time.Time, endTime time.Time) *singleEntry { +func newEntry(command string, args string, user string, version string, startTime time.Time, endTime time.Time, profile ...string) *singleEntry { + p := viper.GetString(config.ProfileName) + if len(profile) > 0 { + p = profile[0] + } return &singleEntry{ args: args, command: command, endTime: endTime.Format(constants.TimeFormat), - profile: viper.GetString(config.ProfileName), + profile: p, startTime: startTime.Format(constants.TimeFormat), user: user, version: version, @@ -103,7 +107,7 @@ func logsToEntries(logs []string) ([]singleEntry, error) { } // entriesToTable converts audit lines into a formatted table. -func entriesToTable(entries []singleEntry, headers []string) (string, error) { +func entriesToTable(entries []singleEntry, headers []string) string { c := [][]string{} for _, e := range entries { c = append(c, e.toFields()) @@ -117,5 +121,5 @@ func entriesToTable(entries []singleEntry, headers []string) (string, error) { t.SetCenterSeparator("|") t.AppendBulk(c) t.Render() - return b.String(), nil + return b.String() } diff --git a/pkg/minikube/audit/entry_test.go b/pkg/minikube/audit/entry_test.go index 1d7867014a..c8d8b6c562 100644 --- a/pkg/minikube/audit/entry_test.go +++ b/pkg/minikube/audit/entry_test.go @@ -17,12 +17,12 @@ limitations under the License. package audit import ( + "encoding/json" + "fmt" "strings" "testing" "time" - "github.com/spf13/viper" - "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" ) @@ -31,37 +31,32 @@ func TestEntry(t *testing.T) { a := "--alsologtostderr" p := "profile1" u := "user1" + v := "v0.17.1" st := time.Now() stFormatted := st.Format(constants.TimeFormat) et := time.Now() etFormatted := et.Format(constants.TimeFormat) - // save current profile in case something depends on it - oldProfile := viper.GetString(config.ProfileName) - viper.Set(config.ProfileName, p) - e := newEntry(c, a, u, st, et) - viper.Set(config.ProfileName, oldProfile) + e := newEntry(c, a, u, v, st, et, p) t.Run("TestNewEntry", func(t *testing.T) { - d := e.Data - tests := []struct { key string + got string want string }{ - {"command", c}, - {"args", a}, - {"profile", p}, - {"user", u}, - {"startTime", stFormatted}, - {"endTime", etFormatted}, + {"command", e.command, c}, + {"args", e.args, a}, + {"profile", e.profile, p}, + {"user", e.user, u}, + {"version", e.version, v}, + {"startTime", e.startTime, stFormatted}, + {"endTime", e.endTime, etFormatted}, } for _, tt := range tests { - got := d[tt.key] - - if got != tt.want { - t.Errorf("Data[%q] = %s; want %s", tt.key, got, tt.want) + if tt.got != tt.want { + t.Errorf("singleEntry.%s = %s; want %s", tt.key, tt.got, tt.want) } } }) @@ -75,14 +70,70 @@ func TestEntry(t *testing.T) { } }) + t.Run("TestToMap", func(t *testing.T) { + m := e.toMap() + + tests := []struct { + key string + want string + }{ + {"command", c}, + {"args", a}, + {"profile", p}, + {"user", u}, + {"version", v}, + {"startTime", stFormatted}, + {"endTime", etFormatted}, + } + + for _, tt := range tests { + got := m[tt.key] + if got != tt.want { + t.Errorf("map[%s] = %s; want %s", tt.key, got, tt.want) + } + } + }) + t.Run("TestToField", func(t *testing.T) { got := e.toFields() gotString := strings.Join(got, ",") - want := []string{c, a, p, u, stFormatted, etFormatted} + want := []string{c, a, p, u, v, stFormatted, etFormatted} wantString := strings.Join(want, ",") if gotString != wantString { t.Errorf("toFields() = %s; want %s", gotString, wantString) } }) + + t.Run("TestAssignFields", func(t *testing.T) { + l := fmt.Sprintf(`{"data":{"args":"%s","command":"%s","endTime":"%s","profile":"%s","startTime":"%s","user":"%s","version":"v0.17.1"},"datacontenttype":"application/json","id":"bc6ec9d4-0d08-4b57-ac3b-db8d67774768","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.audit"}`, a, c, etFormatted, p, stFormatted, u) + + e := &singleEntry{} + if err := json.Unmarshal([]byte(l), e); err != nil { + t.Fatalf("failed to unmarshal log:: %v", err) + } + + e.assignFields() + + tests := []struct { + key string + got string + want string + }{ + {"command", e.command, c}, + {"args", e.args, a}, + {"profile", e.profile, p}, + {"user", e.user, u}, + {"version", e.version, v}, + {"startTime", e.startTime, stFormatted}, + {"endTime", e.endTime, etFormatted}, + } + + for _, tt := range tests { + if tt.got != tt.want { + t.Errorf("singleEntry.%s = %s; want %s", tt.key, tt.got, tt.want) + + } + } + }) } diff --git a/pkg/minikube/audit/logFile_test.go b/pkg/minikube/audit/logFile_test.go index ccea362767..90c8faba4f 100644 --- a/pkg/minikube/audit/logFile_test.go +++ b/pkg/minikube/audit/logFile_test.go @@ -42,7 +42,7 @@ func TestLogFile(t *testing.T) { defer func() { currentLogFile = &oldLogFile }() currentLogFile = f - e := newEntry("start", "-v", "user1", time.Now(), time.Now()) + e := newEntry("start", "-v", "user1", "v0.17.1", time.Now(), time.Now()) if err := appendToLog(e); err != nil { t.Fatalf("Error appendingToLog: %v", err) } diff --git a/pkg/minikube/audit/report.go b/pkg/minikube/audit/report.go index 3b316e86dd..c4e8a211e9 100644 --- a/pkg/minikube/audit/report.go +++ b/pkg/minikube/audit/report.go @@ -60,10 +60,6 @@ func Report(lines int) (*Data, error) { } // Table creates a formatted table using entries from the report. -func (r *Data) Table() (string, error) { - t, err := entriesToTable(r.entries, r.headers) - if err != nil { - return "", fmt.Errorf("failed to convert logs to table: %v", err) - } - return t, nil +func (r *Data) Table() string { + return entriesToTable(r.entries, r.headers) } diff --git a/pkg/minikube/audit/report_test.go b/pkg/minikube/audit/report_test.go index 4dd727ecdd..a6547b0d2b 100644 --- a/pkg/minikube/audit/report_test.go +++ b/pkg/minikube/audit/report_test.go @@ -50,13 +50,7 @@ func TestAuditReport(t *testing.T) { t.Fatalf("failed to create report: %v", err) } - if len(r.logs) != wantedLines { - t.Fatalf("report has %d lines of logs, want %d", len(r.logs), wantedLines) + if len(r.entries) != wantedLines { + t.Fatalf("report has %d lines of logs, want %d", len(r.entries), wantedLines) } - - t.Run("TestTable", func(t *testing.T) { - if _, err := r.Table(); err != nil { - t.Errorf("failed to create table from report: %v", err) - } - }) } diff --git a/pkg/minikube/logs/logs.go b/pkg/minikube/logs/logs.go index 4fcac53b7e..568f26ee7a 100644 --- a/pkg/minikube/logs/logs.go +++ b/pkg/minikube/logs/logs.go @@ -208,11 +208,7 @@ func outputAudit(lines int) error { if err != nil { return fmt.Errorf("failed to create audit report with error: %v", err) } - t, err := r.Table() - if err != nil { - return fmt.Errorf("failed to create audit table with error: %v", err) - } - out.Step(style.Empty, t) + out.Step(style.Empty, r.Table()) return nil } From 1c585e2885d0538cd3c492463407b111067639e5 Mon Sep 17 00:00:00 2001 From: Steven Powell Date: Wed, 17 Feb 2021 13:49:22 -0700 Subject: [PATCH 06/10] renamed funcs and vars --- pkg/minikube/audit/audit.go | 4 +- pkg/minikube/audit/logFile.go | 6 +-- pkg/minikube/audit/logFile_test.go | 4 +- pkg/minikube/audit/report.go | 20 ++++---- pkg/minikube/audit/report_test.go | 4 +- pkg/minikube/audit/{entry.go => row.go} | 46 ++++++++--------- .../audit/{entry_test.go => row_test.go} | 50 +++++++++---------- pkg/minikube/logs/logs.go | 2 +- 8 files changed, 67 insertions(+), 69 deletions(-) rename pkg/minikube/audit/{entry.go => row.go} (70%) rename pkg/minikube/audit/{entry_test.go => row_test.go} (75%) diff --git a/pkg/minikube/audit/audit.go b/pkg/minikube/audit/audit.go index 7b7e9d2d95..d1c5d07d73 100644 --- a/pkg/minikube/audit/audit.go +++ b/pkg/minikube/audit/audit.go @@ -55,8 +55,8 @@ func Log(startTime time.Time) { if !shouldLog() { return } - e := newEntry(os.Args[1], args(), userName(), version.GetVersion(), startTime, time.Now()) - if err := appendToLog(e); err != nil { + r := newRow(os.Args[1], args(), userName(), version.GetVersion(), startTime, time.Now()) + if err := appendToLog(r); err != nil { klog.Error(err) } } diff --git a/pkg/minikube/audit/logFile.go b/pkg/minikube/audit/logFile.go index af6e2741a9..0cadd67df3 100644 --- a/pkg/minikube/audit/logFile.go +++ b/pkg/minikube/audit/logFile.go @@ -39,14 +39,14 @@ func setLogFile() error { } // appendToLog appends the audit entry to the log file. -func appendToLog(entry *singleEntry) error { +func appendToLog(row *row) error { if currentLogFile == nil { if err := setLogFile(); err != nil { return err } } - e := register.CloudEvent(entry, entry.toMap()) - bs, err := e.MarshalJSON() + ce := register.CloudEvent(row, row.toMap()) + bs, err := ce.MarshalJSON() if err != nil { return fmt.Errorf("error marshalling event: %v", err) } diff --git a/pkg/minikube/audit/logFile_test.go b/pkg/minikube/audit/logFile_test.go index 90c8faba4f..1d83a67281 100644 --- a/pkg/minikube/audit/logFile_test.go +++ b/pkg/minikube/audit/logFile_test.go @@ -42,8 +42,8 @@ func TestLogFile(t *testing.T) { defer func() { currentLogFile = &oldLogFile }() currentLogFile = f - e := newEntry("start", "-v", "user1", "v0.17.1", time.Now(), time.Now()) - if err := appendToLog(e); err != nil { + r := newRow("start", "-v", "user1", "v0.17.1", time.Now(), time.Now()) + if err := appendToLog(r); err != nil { t.Fatalf("Error appendingToLog: %v", err) } diff --git a/pkg/minikube/audit/report.go b/pkg/minikube/audit/report.go index c4e8a211e9..4b07ed8eb6 100644 --- a/pkg/minikube/audit/report.go +++ b/pkg/minikube/audit/report.go @@ -21,13 +21,13 @@ import ( "fmt" ) -type Data struct { +type RawReport struct { headers []string - entries []singleEntry + rows []row } // Report is created from the log file. -func Report(lines int) (*Data, error) { +func Report(lines int) (*RawReport, error) { if lines <= 0 { return nil, fmt.Errorf("number of lines must be 1 or greater") } @@ -48,18 +48,18 @@ func Report(lines int) (*Data, error) { if err := s.Err(); err != nil { return nil, fmt.Errorf("failed to read from audit file: %v", err) } - e, err := logsToEntries(logs) + rows, err := logsToRows(logs) if err != nil { - return nil, fmt.Errorf("failed to convert logs to entries: %v", err) + return nil, fmt.Errorf("failed to convert logs to rows: %v", err) } - r := &Data{ + r := &RawReport{ []string{"Command", "Args", "Profile", "User", "Version", "Start Time", "End Time"}, - e, + rows, } return r, nil } -// Table creates a formatted table using entries from the report. -func (r *Data) Table() string { - return entriesToTable(r.entries, r.headers) +// ASCIITable creates a formatted table using the headers and rows from the report. +func (rr *RawReport) ASCIITable() string { + return rowsToTable(rr.rows, rr.headers) } diff --git a/pkg/minikube/audit/report_test.go b/pkg/minikube/audit/report_test.go index a6547b0d2b..2d77ad0145 100644 --- a/pkg/minikube/audit/report_test.go +++ b/pkg/minikube/audit/report_test.go @@ -50,7 +50,7 @@ func TestAuditReport(t *testing.T) { t.Fatalf("failed to create report: %v", err) } - if len(r.entries) != wantedLines { - t.Fatalf("report has %d lines of logs, want %d", len(r.entries), wantedLines) + if len(r.rows) != wantedLines { + t.Fatalf("report has %d lines of logs, want %d", len(r.rows), wantedLines) } } diff --git a/pkg/minikube/audit/entry.go b/pkg/minikube/audit/row.go similarity index 70% rename from pkg/minikube/audit/entry.go rename to pkg/minikube/audit/row.go index 58503be55c..4c6154aa20 100644 --- a/pkg/minikube/audit/entry.go +++ b/pkg/minikube/audit/row.go @@ -24,13 +24,12 @@ import ( "github.com/olekukonko/tablewriter" "github.com/spf13/viper" - "k8s.io/klog" "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/constants" ) -// singleEntry is the log entry of a single command. -type singleEntry struct { +// row is the log of a single command. +type row struct { args string command string endTime string @@ -42,12 +41,12 @@ type singleEntry struct { } // Type returns the cloud events compatible type of this struct. -func (e *singleEntry) Type() string { +func (e *row) Type() string { return "io.k8s.sigs.minikube.audit" } // assignFields converts the map values to their proper fields -func (e *singleEntry) assignFields() { +func (e *row) assignFields() { e.args = e.Data["args"] e.command = e.Data["command"] e.endTime = e.Data["endTime"] @@ -58,7 +57,7 @@ func (e *singleEntry) assignFields() { } // toMap combines fields into a string map -func (e *singleEntry) toMap() map[string]string { +func (e *row) toMap() map[string]string { return map[string]string{ "args": e.args, "command": e.command, @@ -70,13 +69,13 @@ func (e *singleEntry) toMap() map[string]string { } } -// newEntry returns a new audit type. -func newEntry(command string, args string, user string, version string, startTime time.Time, endTime time.Time, profile ...string) *singleEntry { +// newRow returns a new audit type. +func newRow(command string, args string, user string, version string, startTime time.Time, endTime time.Time, profile ...string) *row { p := viper.GetString(config.ProfileName) if len(profile) > 0 { p = profile[0] } - return &singleEntry{ + return &row{ args: args, command: command, endTime: endTime.Format(constants.TimeFormat), @@ -87,32 +86,31 @@ func newEntry(command string, args string, user string, version string, startTim } } -// toFields converts an entry to an array of fields. -func (e *singleEntry) toFields() []string { +// toFields converts a row to an array of fields. +func (e *row) toFields() []string { return []string{e.command, e.args, e.profile, e.user, e.version, e.startTime, e.endTime} } -// logsToEntries converts audit logs into arrays of entries. -func logsToEntries(logs []string) ([]singleEntry, error) { - c := []singleEntry{} +// logsToRows converts audit logs into arrays of rows. +func logsToRows(logs []string) ([]row, error) { + rows := []row{} for _, l := range logs { - e := singleEntry{} - if err := json.Unmarshal([]byte(l), &e); err != nil { + r := row{} + if err := json.Unmarshal([]byte(l), &r); err != nil { return nil, fmt.Errorf("failed to unmarshal %q: %v", l, err) } - e.assignFields() - c = append(c, e) + r.assignFields() + rows = append(rows, r) } - return c, nil + return rows, nil } -// entriesToTable converts audit lines into a formatted table. -func entriesToTable(entries []singleEntry, headers []string) string { +// rowsToTable converts audit rows into a formatted table. +func rowsToTable(rows []row, headers []string) string { c := [][]string{} - for _, e := range entries { - c = append(c, e.toFields()) + for _, r := range rows { + c = append(c, r.toFields()) } - klog.Info(c) b := new(bytes.Buffer) t := tablewriter.NewWriter(b) t.SetHeader(headers) diff --git a/pkg/minikube/audit/entry_test.go b/pkg/minikube/audit/row_test.go similarity index 75% rename from pkg/minikube/audit/entry_test.go rename to pkg/minikube/audit/row_test.go index c8d8b6c562..50fe990396 100644 --- a/pkg/minikube/audit/entry_test.go +++ b/pkg/minikube/audit/row_test.go @@ -26,7 +26,7 @@ import ( "k8s.io/minikube/pkg/minikube/constants" ) -func TestEntry(t *testing.T) { +func TestRow(t *testing.T) { c := "start" a := "--alsologtostderr" p := "profile1" @@ -37,32 +37,32 @@ func TestEntry(t *testing.T) { et := time.Now() etFormatted := et.Format(constants.TimeFormat) - e := newEntry(c, a, u, v, st, et, p) + r := newRow(c, a, u, v, st, et, p) - t.Run("TestNewEntry", func(t *testing.T) { + t.Run("TestNewRow", func(t *testing.T) { tests := []struct { key string got string want string }{ - {"command", e.command, c}, - {"args", e.args, a}, - {"profile", e.profile, p}, - {"user", e.user, u}, - {"version", e.version, v}, - {"startTime", e.startTime, stFormatted}, - {"endTime", e.endTime, etFormatted}, + {"command", r.command, c}, + {"args", r.args, a}, + {"profile", r.profile, p}, + {"user", r.user, u}, + {"version", r.version, v}, + {"startTime", r.startTime, stFormatted}, + {"endTime", r.endTime, etFormatted}, } for _, tt := range tests { if tt.got != tt.want { - t.Errorf("singleEntry.%s = %s; want %s", tt.key, tt.got, tt.want) + t.Errorf("row.%s = %s; want %s", tt.key, tt.got, tt.want) } } }) t.Run("TestType", func(t *testing.T) { - got := e.Type() + got := r.Type() want := "io.k8s.sigs.minikube.audit" if got != want { @@ -71,7 +71,7 @@ func TestEntry(t *testing.T) { }) t.Run("TestToMap", func(t *testing.T) { - m := e.toMap() + m := r.toMap() tests := []struct { key string @@ -95,7 +95,7 @@ func TestEntry(t *testing.T) { }) t.Run("TestToField", func(t *testing.T) { - got := e.toFields() + got := r.toFields() gotString := strings.Join(got, ",") want := []string{c, a, p, u, v, stFormatted, etFormatted} wantString := strings.Join(want, ",") @@ -108,25 +108,25 @@ func TestEntry(t *testing.T) { t.Run("TestAssignFields", func(t *testing.T) { l := fmt.Sprintf(`{"data":{"args":"%s","command":"%s","endTime":"%s","profile":"%s","startTime":"%s","user":"%s","version":"v0.17.1"},"datacontenttype":"application/json","id":"bc6ec9d4-0d08-4b57-ac3b-db8d67774768","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.audit"}`, a, c, etFormatted, p, stFormatted, u) - e := &singleEntry{} - if err := json.Unmarshal([]byte(l), e); err != nil { - t.Fatalf("failed to unmarshal log:: %v", err) + r := &row{} + if err := json.Unmarshal([]byte(l), r); err != nil { + t.Fatalf("failed to unmarshal log: %v", err) } - e.assignFields() + r.assignFields() tests := []struct { key string got string want string }{ - {"command", e.command, c}, - {"args", e.args, a}, - {"profile", e.profile, p}, - {"user", e.user, u}, - {"version", e.version, v}, - {"startTime", e.startTime, stFormatted}, - {"endTime", e.endTime, etFormatted}, + {"command", r.command, c}, + {"args", r.args, a}, + {"profile", r.profile, p}, + {"user", r.user, u}, + {"version", r.version, v}, + {"startTime", r.startTime, stFormatted}, + {"endTime", r.endTime, etFormatted}, } for _, tt := range tests { diff --git a/pkg/minikube/logs/logs.go b/pkg/minikube/logs/logs.go index 568f26ee7a..d4a528681f 100644 --- a/pkg/minikube/logs/logs.go +++ b/pkg/minikube/logs/logs.go @@ -208,7 +208,7 @@ func outputAudit(lines int) error { if err != nil { return fmt.Errorf("failed to create audit report with error: %v", err) } - out.Step(style.Empty, r.Table()) + out.Step(style.Empty, r.ASCIITable()) return nil } From 04b03439cdaa0b6d059ec31d6ad873045bc2ccfc Mon Sep 17 00:00:00 2001 From: Steven Powell Date: Wed, 17 Feb 2021 13:52:49 -0700 Subject: [PATCH 07/10] updated test names --- pkg/minikube/audit/report_test.go | 2 +- pkg/minikube/audit/row_test.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/minikube/audit/report_test.go b/pkg/minikube/audit/report_test.go index 2d77ad0145..f5f3e8012a 100644 --- a/pkg/minikube/audit/report_test.go +++ b/pkg/minikube/audit/report_test.go @@ -22,7 +22,7 @@ import ( "testing" ) -func TestAuditReport(t *testing.T) { +func TestReport(t *testing.T) { f, err := ioutil.TempFile("", "audit.json") if err != nil { t.Fatalf("failed creating temporary file: %v", err) diff --git a/pkg/minikube/audit/row_test.go b/pkg/minikube/audit/row_test.go index 50fe990396..7e80a5febd 100644 --- a/pkg/minikube/audit/row_test.go +++ b/pkg/minikube/audit/row_test.go @@ -39,7 +39,7 @@ func TestRow(t *testing.T) { r := newRow(c, a, u, v, st, et, p) - t.Run("TestNewRow", func(t *testing.T) { + t.Run("NewRow", func(t *testing.T) { tests := []struct { key string got string @@ -61,7 +61,7 @@ func TestRow(t *testing.T) { } }) - t.Run("TestType", func(t *testing.T) { + t.Run("Type", func(t *testing.T) { got := r.Type() want := "io.k8s.sigs.minikube.audit" @@ -70,7 +70,7 @@ func TestRow(t *testing.T) { } }) - t.Run("TestToMap", func(t *testing.T) { + t.Run("ToMap", func(t *testing.T) { m := r.toMap() tests := []struct { @@ -94,7 +94,7 @@ func TestRow(t *testing.T) { } }) - t.Run("TestToField", func(t *testing.T) { + t.Run("ToFields", func(t *testing.T) { got := r.toFields() gotString := strings.Join(got, ",") want := []string{c, a, p, u, v, stFormatted, etFormatted} @@ -105,7 +105,7 @@ func TestRow(t *testing.T) { } }) - t.Run("TestAssignFields", func(t *testing.T) { + t.Run("AssignFields", func(t *testing.T) { l := fmt.Sprintf(`{"data":{"args":"%s","command":"%s","endTime":"%s","profile":"%s","startTime":"%s","user":"%s","version":"v0.17.1"},"datacontenttype":"application/json","id":"bc6ec9d4-0d08-4b57-ac3b-db8d67774768","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.audit"}`, a, c, etFormatted, p, stFormatted, u) r := &row{} From 0f8f40b340101ac69ae155dc52e8eb90d95e6fa7 Mon Sep 17 00:00:00 2001 From: Steven Powell Date: Wed, 17 Feb 2021 15:02:00 -0700 Subject: [PATCH 08/10] added integration tests --- test/integration/functional_test.go | 2 +- test/integration/version_upgrade_test.go | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/test/integration/functional_test.go b/test/integration/functional_test.go index d52ae2f864..e279ae4a43 100644 --- a/test/integration/functional_test.go +++ b/test/integration/functional_test.go @@ -722,7 +722,7 @@ func validateLogsCmd(ctx context.Context, t *testing.T, profile string) { if err != nil { t.Errorf("%s failed: %v", rr.Command(), err) } - expectedWords := []string{"apiserver", "Linux", "kubelet"} + expectedWords := []string{"apiserver", "Linux", "kubelet", "Audit"} switch ContainerRuntime() { case "docker": expectedWords = append(expectedWords, "Docker") diff --git a/test/integration/version_upgrade_test.go b/test/integration/version_upgrade_test.go index 564803e87d..055fc59da5 100644 --- a/test/integration/version_upgrade_test.go +++ b/test/integration/version_upgrade_test.go @@ -196,6 +196,13 @@ func TestStoppedBinaryUpgrade(t *testing.T) { if err != nil { t.Fatalf("upgrade from %s to HEAD failed: %s: %v", legacyVersion, rr.Command(), err) } + + t.Run("MinikubeLogs", func(t *testing.T) { + rr, err = Run(t, exec.CommandContext(ctx, Target(), "logs")) + if err != nil { + t.Fatalf("minikube logs after upgrade to HEAD failed: %v", err) + } + }) } // TestKubernetesUpgrade upgrades Kubernetes from oldest to newest From c5c1ee90138fd900b303fca6fbbbbe1bdb849de4 Mon Sep 17 00:00:00 2001 From: Steven Powell Date: Thu, 18 Feb 2021 18:13:17 -0700 Subject: [PATCH 09/10] improved comments, var names & error messages --- pkg/minikube/audit/logFile.go | 2 +- pkg/minikube/audit/report.go | 13 +++++++------ pkg/minikube/audit/report_test.go | 2 +- pkg/minikube/audit/row.go | 15 +++++++++------ pkg/minikube/audit/row_test.go | 6 +++--- pkg/minikube/logs/logs.go | 2 +- test/integration/version_upgrade_test.go | 2 +- 7 files changed, 23 insertions(+), 19 deletions(-) diff --git a/pkg/minikube/audit/logFile.go b/pkg/minikube/audit/logFile.go index 0cadd67df3..1762070f1e 100644 --- a/pkg/minikube/audit/logFile.go +++ b/pkg/minikube/audit/logFile.go @@ -38,7 +38,7 @@ func setLogFile() error { return nil } -// appendToLog appends the audit entry to the log file. +// appendToLog appends the row to the log file. func appendToLog(row *row) error { if currentLogFile == nil { if err := setLogFile(); err != nil { diff --git a/pkg/minikube/audit/report.go b/pkg/minikube/audit/report.go index 4b07ed8eb6..6816757126 100644 --- a/pkg/minikube/audit/report.go +++ b/pkg/minikube/audit/report.go @@ -21,15 +21,16 @@ import ( "fmt" ) +// RawReport contains the information required to generate formatted reports. type RawReport struct { headers []string rows []row } -// Report is created from the log file. -func Report(lines int) (*RawReport, error) { - if lines <= 0 { - return nil, fmt.Errorf("number of lines must be 1 or greater") +// Report is created using the last n lines from the log file. +func Report(lastNLines int) (*RawReport, error) { + if lastNLines <= 0 { + return nil, fmt.Errorf("last n lines must be 1 or greater") } if currentLogFile == nil { if err := setLogFile(); err != nil { @@ -40,7 +41,7 @@ func Report(lines int) (*RawReport, error) { s := bufio.NewScanner(currentLogFile) for s.Scan() { // pop off the earliest line if already at desired log length - if len(logs) == lines { + if len(logs) == lastNLines { logs = logs[1:] } logs = append(logs, s.Text()) @@ -61,5 +62,5 @@ func Report(lines int) (*RawReport, error) { // ASCIITable creates a formatted table using the headers and rows from the report. func (rr *RawReport) ASCIITable() string { - return rowsToTable(rr.rows, rr.headers) + return rowsToASCIITable(rr.rows, rr.headers) } diff --git a/pkg/minikube/audit/report_test.go b/pkg/minikube/audit/report_test.go index f5f3e8012a..cb5cbfe0af 100644 --- a/pkg/minikube/audit/report_test.go +++ b/pkg/minikube/audit/report_test.go @@ -51,6 +51,6 @@ func TestReport(t *testing.T) { } if len(r.rows) != wantedLines { - t.Fatalf("report has %d lines of logs, want %d", len(r.rows), wantedLines) + t.Errorf("report has %d lines of logs, want %d", len(r.rows), wantedLines) } } diff --git a/pkg/minikube/audit/row.go b/pkg/minikube/audit/row.go index 4c6154aa20..fb5991a008 100644 --- a/pkg/minikube/audit/row.go +++ b/pkg/minikube/audit/row.go @@ -45,7 +45,8 @@ func (e *row) Type() string { return "io.k8s.sigs.minikube.audit" } -// assignFields converts the map values to their proper fields +// assignFields converts the map values to their proper fields, +// to be used when converting from JSON Cloud Event format. func (e *row) assignFields() { e.args = e.Data["args"] e.command = e.Data["command"] @@ -56,7 +57,8 @@ func (e *row) assignFields() { e.version = e.Data["version"] } -// toMap combines fields into a string map +// toMap combines fields into a string map, +// to be used when converting to JSON Cloud Event format. func (e *row) toMap() map[string]string { return map[string]string{ "args": e.args, @@ -69,7 +71,7 @@ func (e *row) toMap() map[string]string { } } -// newRow returns a new audit type. +// newRow creates a new audit row. func newRow(command string, args string, user string, version string, startTime time.Time, endTime time.Time, profile ...string) *row { p := viper.GetString(config.ProfileName) if len(profile) > 0 { @@ -86,7 +88,8 @@ func newRow(command string, args string, user string, version string, startTime } } -// toFields converts a row to an array of fields. +// toFields converts a row to an array of fields, +// to be used when converting to a table. func (e *row) toFields() []string { return []string{e.command, e.args, e.profile, e.user, e.version, e.startTime, e.endTime} } @@ -105,8 +108,8 @@ func logsToRows(logs []string) ([]row, error) { return rows, nil } -// rowsToTable converts audit rows into a formatted table. -func rowsToTable(rows []row, headers []string) string { +// rowsToASCIITable converts rows into a formatted ASCII table. +func rowsToASCIITable(rows []row, headers []string) string { c := [][]string{} for _, r := range rows { c = append(c, r.toFields()) diff --git a/pkg/minikube/audit/row_test.go b/pkg/minikube/audit/row_test.go index 7e80a5febd..88b54174df 100644 --- a/pkg/minikube/audit/row_test.go +++ b/pkg/minikube/audit/row_test.go @@ -70,7 +70,7 @@ func TestRow(t *testing.T) { } }) - t.Run("ToMap", func(t *testing.T) { + t.Run("toMap", func(t *testing.T) { m := r.toMap() tests := []struct { @@ -94,7 +94,7 @@ func TestRow(t *testing.T) { } }) - t.Run("ToFields", func(t *testing.T) { + t.Run("toFields", func(t *testing.T) { got := r.toFields() gotString := strings.Join(got, ",") want := []string{c, a, p, u, v, stFormatted, etFormatted} @@ -105,7 +105,7 @@ func TestRow(t *testing.T) { } }) - t.Run("AssignFields", func(t *testing.T) { + t.Run("assignFields", func(t *testing.T) { l := fmt.Sprintf(`{"data":{"args":"%s","command":"%s","endTime":"%s","profile":"%s","startTime":"%s","user":"%s","version":"v0.17.1"},"datacontenttype":"application/json","id":"bc6ec9d4-0d08-4b57-ac3b-db8d67774768","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.audit"}`, a, c, etFormatted, p, stFormatted, u) r := &row{} diff --git a/pkg/minikube/logs/logs.go b/pkg/minikube/logs/logs.go index d4a528681f..8f20856249 100644 --- a/pkg/minikube/logs/logs.go +++ b/pkg/minikube/logs/logs.go @@ -206,7 +206,7 @@ func outputAudit(lines int) error { out.Step(style.Empty, "==> Audit <==") r, err := audit.Report(lines) if err != nil { - return fmt.Errorf("failed to create audit report with error: %v", err) + return fmt.Errorf("failed to create audit report: %v", err) } out.Step(style.Empty, r.ASCIITable()) return nil diff --git a/test/integration/version_upgrade_test.go b/test/integration/version_upgrade_test.go index 055fc59da5..9e12e18fb0 100644 --- a/test/integration/version_upgrade_test.go +++ b/test/integration/version_upgrade_test.go @@ -200,7 +200,7 @@ func TestStoppedBinaryUpgrade(t *testing.T) { t.Run("MinikubeLogs", func(t *testing.T) { rr, err = Run(t, exec.CommandContext(ctx, Target(), "logs")) if err != nil { - t.Fatalf("minikube logs after upgrade to HEAD failed: %v", err) + t.Fatalf("`minikube logs` after upgrade to HEAD from %s failed: %v", legacyVersion, err) } }) } From 00a480f06c087abc0240769feb775b671ab980f9 Mon Sep 17 00:00:00 2001 From: Steven Powell Date: Fri, 19 Feb 2021 09:56:35 -0700 Subject: [PATCH 10/10] set profile for test --- test/integration/version_upgrade_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/integration/version_upgrade_test.go b/test/integration/version_upgrade_test.go index 9e12e18fb0..01f02e122c 100644 --- a/test/integration/version_upgrade_test.go +++ b/test/integration/version_upgrade_test.go @@ -198,7 +198,8 @@ func TestStoppedBinaryUpgrade(t *testing.T) { } t.Run("MinikubeLogs", func(t *testing.T) { - rr, err = Run(t, exec.CommandContext(ctx, Target(), "logs")) + args := []string{"logs", "-p", profile} + rr, err = Run(t, exec.CommandContext(ctx, Target(), args...)) if err != nil { t.Fatalf("`minikube logs` after upgrade to HEAD from %s failed: %v", legacyVersion, err) }