Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CLOUDGA-24634] Improved formatting for DB Audit logging #271

Merged
merged 1 commit into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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