Skip to content

Commit

Permalink
Improved formatting for DB Audit logging (#271)
Browse files Browse the repository at this point in the history
  • Loading branch information
Siddarth-Baldwa authored Nov 13, 2024
1 parent a2893bc commit 0bbbe97
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 133 deletions.
31 changes: 10 additions & 21 deletions cmd/cluster/audit-log-exporter/db_audit_log_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package audit_log_exporter

import (
"fmt"
"os"
"strconv"
"strings"

Expand All @@ -27,7 +26,7 @@ import (
"github.com/yugabyte/ybm-cli/cmd/util"
ybmAuthClient "github.com/yugabyte/ybm-cli/internal/client"
"github.com/yugabyte/ybm-cli/internal/formatter"
openapi "github.com/yugabyte/yugabytedb-managed-go-client-internal"

ybmclient "github.com/yugabyte/yugabytedb-managed-go-client-internal"
)

Expand Down Expand Up @@ -106,12 +105,7 @@ var enableDbAuditLoggingCmd = &cobra.Command{
fmt.Println(msg)
}

dbAuditLogsExporterCtx := formatter.Context{
Output: os.Stdout,
Format: formatter.NewDbAuditLogsExporterFormat(viper.GetString("output")),
}

formatter.DbAuditLogsExporterWrite(dbAuditLogsExporterCtx, []openapi.DbAuditExporterConfigurationData{respData})
formatter.DbAuditLoggingWriteFull(respData, integrationName)
},
}

Expand Down Expand Up @@ -179,13 +173,7 @@ var updateDbAuditLoggingCmd = &cobra.Command{
} else {
fmt.Println(msg)
}

dbAuditLogsExporterCtx := formatter.Context{
Output: os.Stdout,
Format: formatter.NewDbAuditLogsExporterFormat(viper.GetString("output")),
}

formatter.DbAuditLogsExporterWrite(dbAuditLogsExporterCtx, []openapi.DbAuditExporterConfigurationData{respData})
formatter.DbAuditLoggingWriteFull(respData, integrationName)
},
}

Expand Down Expand Up @@ -214,17 +202,18 @@ var describeDbAuditLoggingCmd = &cobra.Command{
logrus.Fatalf(ybmAuthClient.GetApiErrorDetails(err))
}

dbAuditLogsExporterCtx := formatter.Context{
Output: os.Stdout,
Format: formatter.NewDbAuditLogsExporterFormat(viper.GetString("output")),
}

if len(resp.GetData()) < 1 {
fmt.Println("No DB Audit Logs Exporter found")
return
}

formatter.DbAuditLogsExporterWrite(dbAuditLogsExporterCtx, resp.GetData())
integrationId := resp.GetData()[0].Spec.ExporterId
integrationName, err := authApi.GetIntegrationNameFromId(integrationId)
if err != nil {
logrus.Debugf("could not fetch associated name for integration id: %s", integrationId)
}

formatter.DbAuditLoggingWriteFull(resp.GetData()[0], integrationName)
},
}

Expand Down
54 changes: 46 additions & 8 deletions cmd/db_audit_logs_exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"net/http"
"os"
"os/exec"
"regexp"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
Expand Down Expand Up @@ -86,9 +87,20 @@ var _ = Describe("DB Audit Logging", func() {
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
Expect(err).NotTo(HaveOccurred())
session.Wait(2)
Expect(session.Out).Should(gbytes.Say(`Db audit logging is being enabled for cluster stunning-sole
ID Date Created Cluster ID Integration ID State Ysql Config
9e3fabbc-849c-4a77-bdb2-9422e712e7dc 2024-02-27T06:30:51.304Z 5f80730f-ba3f-4f7e-8c01-f8fa4c90dad8 7c07c103-e3b2-48b6-ac30-764e9b5275e1 ACTIVE {\"log_settings\":{\"log_catalog\":true,\"log_client\":true,\"log_level\":\"LOG\",\"log_parameter\":false,\"log_relation\":false,\"log_statement_once\":false},\"statement_classes\":\[\"READ\",\"WRITE\"]}`))

Expect(session.Out).Should(gbytes.Say(regexp.QuoteMeta(`Db audit logging is being enabled for cluster stunning-sole
State Integration Name
ACTIVE datadog-tp
Ysql Config Key Ysql Config Value
statement-classes [READ, WRITE]
log-catalog true
log-client true
log-level LOG
log-parameter false
log-relation false
log-statement-once false`)))

session.Kill()
})
It("should return required field name and type when not set", func() {
Expand Down Expand Up @@ -154,12 +166,29 @@ ID Date Created Cluster ID
ghttp.RespondWithJSONEncodedPtr(&statusCode, responseDbAuditList),
),
)
err = loadJson("./test/fixtures/list-telemetry-provider.json", &responseIntegrationList)
Expect(err).ToNot(HaveOccurred())
server.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest(http.MethodGet, "/api/public/v1/accounts/340af43a-8a7c-4659-9258-4876fd6a207b/projects/78d4459c-0f45-47a5-899a-45ddf43eba6e/telemetry-providers"),
ghttp.RespondWithJSONEncodedPtr(&statusCode, responseIntegrationList),
),
)
cmd := exec.Command(compiledCLIPath, "cluster", "db-audit-logging", "describe", "--cluster-name", "stunning-sole")
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
Expect(err).NotTo(HaveOccurred())
session.Wait(2)
Expect(session.Out).Should(gbytes.Say(`ID Date Created Cluster ID Integration ID State Ysql Config
9e3fabbc-849c-4a77-bdb2-9422e712e7dc 2024-02-27T06:30:51.304Z 5f80730f-ba3f-4f7e-8c01-f8fa4c90dad8 7c07c103-e3b2-48b6-ac30-764e9b5275e1 ACTIVE {\"log_settings\":{\"log_catalog\":true,\"log_client\":true,\"log_level\":\"LOG\",\"log_parameter\":false,\"log_relation\":false,\"log_statement_once\":false},\"statement_classes\":\[\"READ\",\"WRITE\"]}`))
Expect(session.Out).Should(gbytes.Say(regexp.QuoteMeta(`State Integration Name
ACTIVE datadog-tp
Ysql Config Key Ysql Config Value
statement-classes [READ, WRITE]
log-catalog true
log-client true
log-level LOG
log-parameter false
log-relation false
log-statement-once false`)))
session.Kill()
})
It("should return required field name and type when not set", func() {
Expand Down Expand Up @@ -258,9 +287,18 @@ ID Date Created Cluster ID
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
Expect(err).NotTo(HaveOccurred())
session.Wait(2)
Expect(session.Out).Should(gbytes.Say(`DB audit logging configuration is being updated for cluster stunning-sole
ID Date Created Cluster ID Integration ID State Ysql Config
9e3fabbc-849c-4a77-bdb2-9422e712e7dc 2024-02-27T06:30:51.304Z 5f80730f-ba3f-4f7e-8c01-f8fa4c90dad8 7c07c103-e3b2-48b6-ac30-764e9b5275e1 ACTIVE {\"log_settings\":{\"log_catalog\":false,\"log_client\":true,\"log_level\":\"NOTICE\",\"log_parameter\":false,\"log_relation\":true,\"log_statement_once\":false},\"statement_classes\":\[\"READ\",\"WRITE\"]}`))
Expect(session.Out).Should(gbytes.Say(regexp.QuoteMeta(`DB audit logging configuration is being updated for cluster stunning-sole
State Integration Name
ACTIVE datadog-tp
Ysql Config Key Ysql Config Value
statement-classes [READ, WRITE]
log-catalog false
log-client true
log-level NOTICE
log-parameter false
log-relation true
log-statement-once false`)))
session.Kill()
})
It("should return required field name and type when not set", func() {
Expand Down
2 changes: 1 addition & 1 deletion cmd/test/fixtures/list-db-audit.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"log_statement_once": false
}
},
"exporter_id": "7c07c103-e3b2-48b6-ac30-764e9b5275e1"
"exporter_id": "129f7c97-81ae-47c7-8f9e-40ab4390093f"
},
"info": {
"id": "9e3fabbc-849c-4a77-bdb2-9422e712e7dc",
Expand Down
173 changes: 173 additions & 0 deletions internal/formatter/db_audit_log_exporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// Licensed to Yugabyte, Inc. under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership. Yugabyte
// licenses this file to you 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 formatter

import (
"encoding/json"
"os"
"strconv"
"strings"

"github.com/sirupsen/logrus"
"github.com/spf13/viper"
ybmclient "github.com/yugabyte/yugabytedb-managed-go-client-internal"
)

const defaultDbAuditLoggingConfigListing = "table {{.State}}\t{{.IntegrationName}}"
const ysqlConfigListing = "table {{.YsqlConfigKey}}\t{{.YsqlConfigValue}}"

type DbAuditLoggingContext struct {
HeaderContext
Context
data ybmclient.DbAuditExporterConfigurationData
integrationName string
}

type YsqlConfigListing struct {
HeaderContext
Context
configKey string
configVal string
}

func NewDbAuditLoggingFormat() Format {
source := viper.GetString("output")
switch source {
case "table", "":
format := defaultDbAuditLoggingConfigListing
return Format(format)
default: // custom format or json or pretty
return Format(source)
}
}

func NewYsqlConfigFormat() Format {
source := viper.GetString("output")
switch source {
case "table", "":
format := ysqlConfigListing
return Format(format)
default: // custom format or json or pretty
return Format(source)
}
}

func NewDbAuditLoggingContext() *DbAuditLoggingContext {
DbAuditLoggingContext := DbAuditLoggingContext{}
DbAuditLoggingContext.Header = SubHeaderContext{
"State": "State",
"IntegrationName": "Integration Name",
}
return &DbAuditLoggingContext
}

func NewYsqlConfigContext() *YsqlConfigListing {
YsqlConfigListing := YsqlConfigListing{}
YsqlConfigListing.Header = SubHeaderContext{
"YsqlConfigKey": "Ysql Config Key",
"YsqlConfigValue": "Ysql Config Value",
}
return &YsqlConfigListing
}

func ysqlConfigWrite(ctx Context, ysqlConfig ybmclient.DbAuditYsqlExportConfig) error {
logSettings := ysqlConfig.GetLogSettings()
render := func(format func(subContext SubContext) error) error {
addYsqlConfigRow(format, "statement-classes", convertEnumListToString(ysqlConfig.GetStatementClasses()))
addYsqlConfigRow(format, "log-catalog", strconv.FormatBool(*logSettings.LogCatalog))
addYsqlConfigRow(format, "log-client", strconv.FormatBool(*logSettings.LogClient))
addYsqlConfigRow(format, "log-level", string(*logSettings.LogLevel))
addYsqlConfigRow(format, "log-parameter", strconv.FormatBool(*logSettings.LogParameter))
addYsqlConfigRow(format, "log-relation", strconv.FormatBool(*logSettings.LogRelation))
addYsqlConfigRow(format, "log-statement-once", strconv.FormatBool(*logSettings.LogStatementOnce))
return nil
}
return ctx.Write(NewYsqlConfigContext(), render)
}

func addYsqlConfigRow(format func(subContext SubContext) error, key string, value string) {
err := format(&YsqlConfigListing{configKey: key, configVal: value})
if err != nil {
logrus.Fatal(err)
}
}

func dbAuditLoggingWrite(ctx Context, dbAuditLoggingData ybmclient.DbAuditExporterConfigurationData, integrationName string) error {
render := func(format func(subContext SubContext) error) error {
err := format(&DbAuditLoggingContext{data: dbAuditLoggingData, integrationName: integrationName})
if err != nil {
logrus.Debugf("Error rendering DB Audit Logging configuration data: %v", err)
return err
}
return nil
}
return ctx.Write(NewDbAuditLoggingContext(), render)
}

func DbAuditLoggingWriteFull(dbAuditLoggingData ybmclient.DbAuditExporterConfigurationData, integrationName string) {
ctx := Context{
Output: os.Stdout,
Format: NewDbAuditLoggingFormat(),
}

err := dbAuditLoggingWrite(ctx, dbAuditLoggingData, integrationName)
if err != nil {
logrus.Fatal(err.Error())
}
ctx.Output.Write([]byte("\n"))

// Only render Log config for table output format
if viper.GetString("output") == "table" {
ctx = Context{
Output: os.Stdout,
Format: NewYsqlConfigFormat(),
}

err = ysqlConfigWrite(ctx, dbAuditLoggingData.Spec.YsqlConfig)
if err != nil {
logrus.Fatal(err.Error())
}
}

}

func (context *DbAuditLoggingContext) State() string {
return string(context.data.Info.State)
}

func (context *DbAuditLoggingContext) IntegrationName() string {
return context.integrationName
}

func (context *DbAuditLoggingContext) MarshalJSON() ([]byte, error) {
return json.Marshal(context.data)
}

func (context *YsqlConfigListing) YsqlConfigKey() string {
return string(context.configKey)
}

func (context *YsqlConfigListing) YsqlConfigValue() string {
return string(context.configVal)
}

func convertEnumListToString(list []ybmclient.DbAuditYsqlStatmentClassesEnum) string {
var strList []string
for _, enumValue := range list {
strList = append(strList, string(enumValue))
}
return "[" + strings.Join(strList, ", ") + "]"
}
Loading

0 comments on commit 0bbbe97

Please sign in to comment.