Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/go-build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
cd workloadagentplatform
# this is the hash of the workloadagentplatform submodule
# get the hash by running: go list -m -json github.com/GoogleCloudPlatform/workloadagentplatform@main
git checkout d263b695432a9439950984306e9f15c6ac5f0cd7
git checkout e00b74940f449310713ac0ef882538be101c7044
cd ..
find workloadagentplatform/sharedprotos -type f -exec sed -i 's|"sharedprotos|"workloadagentplatform/sharedprotos|g' {} +
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/go-build-protos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
cd workloadagentplatform
# this is the hash of the workloadagentplatform submodule
# get the hash by running: go list -m -json github.com/GoogleCloudPlatform/workloadagentplatform@main
git checkout d263b695432a9439950984306e9f15c6ac5f0cd7
git checkout e00b74940f449310713ac0ef882538be101c7044
cd ..
find workloadagentplatform/sharedprotos -type f -exec sed -i 's|"sharedprotos|"workloadagentplatform/sharedprotos|g' {} +
env:
Expand Down
2 changes: 1 addition & 1 deletion build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ if [ "${COMPILE_PROTOS}" == "TRUE" ] && [ ! -d "workloadagentplatform" ]; then
cd workloadagentplatform
# this is the hash of the workloadagentplatform submodule
# get the hash by running: go list -m -json github.com/GoogleCloudPlatform/workloadagentplatform@main
git checkout d263b695432a9439950984306e9f15c6ac5f0cd7
git checkout e00b74940f449310713ac0ef882538be101c7044
cd ..
# replace the proto imports in the platform that reference the platform
find workloadagentplatform/sharedprotos -type f -exec sed -i 's|"sharedprotos|"workloadagentplatform/sharedprotos|g' {} +
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ require (
cloud.google.com/go/storage v1.50.0
// Get the version by running:
// go list -m -json github.com/GoogleCloudPlatform/workloadagentplatform/sharedlibraries@main
github.com/GoogleCloudPlatform/workloadagentplatform/sharedlibraries v0.0.0-20250708193908-d263b695432a
github.com/GoogleCloudPlatform/workloadagentplatform/sharedlibraries v0.0.0-20250912122010-e00b74940f44
// Get the version by running:
// go list -m -json github.com/GoogleCloudPlatform/workloadagentplatform/sharedprotos@main
github.com/GoogleCloudPlatform/workloadagentplatform/sharedprotos v0.0.0-20250708193908-d263b695432a
github.com/GoogleCloudPlatform/workloadagentplatform/sharedprotos v0.0.0-20250912122010-e00b74940f44
github.com/SAP/go-hdb v1.12.12
github.com/cenkalti/backoff/v4 v4.3.0
github.com/fsouza/fake-gcs-server v1.52.1
Expand Down
10 changes: 4 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.118.0 h1:tvZe1mgqRxpiVa3XlIGMiPcEUbP1gNXELgD4y/IXmeQ=
cloud.google.com/go v0.118.0/go.mod h1:zIt2pkedt/mo+DQjcT4/L3NDxzHPR29j5HcclNH+9PM=
cloud.google.com/go/aiplatform v1.70.0 h1:vnqsPkgcwlDEpWl9t6C3/HLfHeweuGXs2gcYTzH6dMs=
cloud.google.com/go/aiplatform v1.70.0/go.mod h1:1cewyC4h+yvRs0qVvlCuU3V6j1pJ41doIcroYX3uv8o=
cloud.google.com/go/artifactregistry v1.16.1 h1:ZNXGB6+T7VmWdf6//VqxLdZ/sk0no8W0ujanHeJwDRw=
cloud.google.com/go/artifactregistry v1.16.1/go.mod h1:sPvFPZhfMavpiongKwfg93EOwJ18Tnj9DIwTU9xWUgs=
cloud.google.com/go/auth v0.14.1 h1:AwoJbzUdxA/whv1qj3TLKwh3XX5sikny2fc40wUl+h0=
Expand Down Expand Up @@ -43,10 +41,10 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0/go.mod h1:l2fIqmwB+FKSfvn3bAD/0i+AXAxhIZjTK2svT/mgUXs=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 h1:GYUJLfvd++4DMuMhCFLgLXvFwofIxh/qOwoGuS/LTew=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0/go.mod h1:wRbFgBQUVm1YXrvWKofAEmq9HNJTDphbAaJSSX01KUI=
github.com/GoogleCloudPlatform/workloadagentplatform/sharedlibraries v0.0.0-20250708193908-d263b695432a h1:h4I+QAuiTzcvHkl5OESfP+CV/Y48h0/9YXhC7poPPwU=
github.com/GoogleCloudPlatform/workloadagentplatform/sharedlibraries v0.0.0-20250708193908-d263b695432a/go.mod h1:kd9KRERnc/KakQgqX8jwmVwdMjVcficQIQ1FJ2j6EAQ=
github.com/GoogleCloudPlatform/workloadagentplatform/sharedprotos v0.0.0-20250708193908-d263b695432a h1:FFQ+XGRY3PT3kfw7dKVfKHCUPKIpCKma5cOoIXR9yDM=
github.com/GoogleCloudPlatform/workloadagentplatform/sharedprotos v0.0.0-20250708193908-d263b695432a/go.mod h1:8Ea8vdBuPsWhhwzL9sNK7BFQE9qbkPLZUHxcucWHXaM=
github.com/GoogleCloudPlatform/workloadagentplatform/sharedlibraries v0.0.0-20250912122010-e00b74940f44 h1:lS40AYFAtQmu5EBO7aX1278DuUx815OIziyuFtSbYhQ=
github.com/GoogleCloudPlatform/workloadagentplatform/sharedlibraries v0.0.0-20250912122010-e00b74940f44/go.mod h1:WrwTr9HFkp+nbHba/tv36eCLkjKAeCx7mqiG/wL3PNU=
github.com/GoogleCloudPlatform/workloadagentplatform/sharedprotos v0.0.0-20250912122010-e00b74940f44 h1:FrAq6nq+qkx8Z3KmxyBvihx54OEyFWIFP0d6bWL5cvw=
github.com/GoogleCloudPlatform/workloadagentplatform/sharedprotos v0.0.0-20250912122010-e00b74940f44/go.mod h1:8Ea8vdBuPsWhhwzL9sNK7BFQE9qbkPLZUHxcucWHXaM=
github.com/SAP/go-hdb v1.12.12 h1:pZtsnUU7VNNobksc13F5pGr7W3abiJq/W4v7g7GZpKk=
github.com/SAP/go-hdb v1.12.12/go.mod h1:R6RDbzvPk9gTraxYbzfNcy3XRp3vXFGd5vEopvzr0zQ=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
Expand Down
59 changes: 58 additions & 1 deletion internal/configuration/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"time"
Expand All @@ -43,8 +44,17 @@ type ReadConfigFile func(string) ([]byte, error)
// WriteConfigFile abstracts os.WriteFile function for testability.
type WriteConfigFile func(string, []byte, os.FileMode) error

// StatFile abstracts os.Stat function for testability.
type StatFile func(string) (os.FileInfo, error)

// MkdirAll abstracts os.MkdirAll function for testability.
type MkdirAll func(string, os.FileMode) error

var ros = runtime.GOOS

//go:embed defaultconfigs/configuration.json
var defaultConfigurationContent []byte

//go:embed defaultconfigs/hanamonitoring/default_queries.json
var defaultHMQueriesContent []byte

Expand Down Expand Up @@ -83,6 +93,14 @@ func StorageAgentName() string {
return fmt.Sprintf("google-cloud-sap-agent/%s (GPN: Agent for SAP)", AgentVersion)
}

// Path returns the default configuration file path based on the OS.
func Path() string {
if runtime.GOOS == "windows" {
return WindowsConfigPath
}
return LinuxConfigPath
}

// Read just reads configuration from given file and parses it into config proto.
func Read(path string, read ReadConfigFile) (*cpb.Configuration, error) {
content, err := read(path)
Expand All @@ -102,6 +120,18 @@ func Read(path string, read ReadConfigFile) (*cpb.Configuration, error) {
return config, err
}

// Write writes the contents of a configuration proto to a file at the given path.
func Write(config *cpb.Configuration, path string, write WriteConfigFile) error {
content, err := protojson.MarshalOptions{
Multiline: true,
UseProtoNames: true,
}.Marshal(config)
if err != nil {
log.Logger.Errorw("Failed to marshal configuration proto to JSON", "error", err)
}
return write(path, content, 0644)
}

// ReadFromFile reads the final configuration from the given file. Besides parsing the file,
// it consists of the final HANA Monitoring configuration after parsing all the enabled
// HANA Monitoring queries, by applying overrides wherever necessary, into a proto.
Expand Down Expand Up @@ -364,7 +394,7 @@ func prepareHMConf(config *cpb.HANAMonitoringConfiguration) *cpb.HANAMonitoringC
// enabled/disabled. In case of default queries if there is no override item in the custom query list
// then default query is treated as enabled.
func applyOverrides(defaultHMQueriesList, customHMQueriesList []*cpb.Query) []*cpb.Query {
result := []*cpb.Query{}
var result []*cpb.Query
for _, query := range defaultHMQueriesList {
q := query
q.Enabled = true
Expand Down Expand Up @@ -467,3 +497,30 @@ func validateColumnTypes(col *cpb.Column) error {
}
return nil
}

// EnsureConfigExists ensures that the sapagent configuration file exists.
// If the file does not exist, it creates the file and its parent directories.
func EnsureConfigExists(stat StatFile, mkdirAll MkdirAll, write WriteConfigFile) error {
path := Path()
_, err := stat(path)
if err == nil {
return nil
}

// We expect to see os.ErrNotExist if the file does not exist.
// Any other error is unexpected and should be returned.
if !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("failed to stat configuration file: %w", err)
}

dir := filepath.Dir(path)
if err := mkdirAll(dir, 0755); err != nil {
return fmt.Errorf("failed to create configuration file directory %s: %w", dir, err)
}
if err := write(path, defaultConfigurationContent, 0644); err != nil {
return fmt.Errorf("failed to write default configuration to file %s: %w", path, err)
}

log.Logger.Infow("Default configuration file created", "path", path)
return nil
}
100 changes: 100 additions & 0 deletions internal/configuration/configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ package configuration

import (
_ "embed"
"errors"
"os"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/testing/protocmp"
"go.uber.org/zap/zapcore"

Expand Down Expand Up @@ -76,6 +78,34 @@ var (
testConfigWithSapSystemConfigJSON []byte
)

func TestWrite(t *testing.T) {
config := &cpb.Configuration{
LogLevel: cpb.Configuration_INFO,
LogToCloud: &wpb.BoolValue{Value: true},
ProvideSapHostAgentMetrics: &wpb.BoolValue{Value: true},
}
f, err := os.CreateTemp("", "test_config")
if err != nil {
t.Fatalf("Failed to create temp file: %v", err)
}
defer os.Remove(f.Name())
err = Write(config, f.Name(), os.WriteFile)
if err != nil {
t.Fatalf("Write() got err: %v, want error: %t", err, false)
}
contents, err := os.ReadFile(f.Name())
if err != nil {
t.Fatalf("Failed to read file: %v", err)
}
got := &cpb.Configuration{}
if err := protojson.Unmarshal(contents, got); err != nil {
t.Fatalf("Failed to unmarshal config: %v", err)
}
if diff := cmp.Diff(config, got, protocmp.Transform()); diff != "" {
t.Errorf("Write() returned unexpected diff (-want +got):\n%s", diff)
}
}

func TestReadFromFile(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -1505,3 +1535,73 @@ func TestValidateAgentConfiguration(t *testing.T) {
})
}
}

func TestEnsureConfigExists(t *testing.T) {
tests := []struct {
name string
stat StatFile
mkdirAll MkdirAll
write WriteConfigFile
wantErr error
}{
{
name: "ConfigExists",
stat: func(string) (os.FileInfo, error) {
return nil, nil
},
wantErr: nil,
},
{
name: "StatUnexpectedError",
stat: func(string) (os.FileInfo, error) {
return nil, os.ErrPermission
},
wantErr: os.ErrPermission,
},
{
name: "MkdirAllError",
stat: func(string) (os.FileInfo, error) {
return nil, os.ErrNotExist
},
mkdirAll: func(string, os.FileMode) error {
return os.ErrPermission
},
wantErr: os.ErrPermission,
},
{
name: "WriteError",
stat: func(string) (os.FileInfo, error) {
return nil, os.ErrNotExist
},
mkdirAll: func(string, os.FileMode) error {
return nil
},
write: func(string, []byte, os.FileMode) error {
return os.ErrPermission
},
wantErr: os.ErrPermission,
},
{
name: "CreateConfigSuccess",
stat: func(string) (os.FileInfo, error) {
return nil, os.ErrNotExist
},
mkdirAll: func(string, os.FileMode) error {
return nil
},
write: func(string, []byte, os.FileMode) error {
return nil
},
wantErr: nil,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
err := EnsureConfigExists(test.stat, test.mkdirAll, test.write)
if !errors.Is(err, test.wantErr) {
t.Errorf("EnsureConfigExists() returned error: %v, want error: %v", err, test.wantErr)
}
})
}
}
15 changes: 15 additions & 0 deletions internal/configuration/defaultconfigs/configuration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"provide_sap_host_agent_metrics": true,
"log_level": "INFO",
"log_to_cloud": true,
"collection_configuration": {
"collect_workload_validation_metrics": true,
"collect_process_metrics": false
},
"discovery_configuration": {
"enable_discovery": true
},
"hana_monitoring_configuration": {
"enabled": false
}
}
11 changes: 11 additions & 0 deletions internal/startdaemon/test_data/default_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"provide_sap_host_agent_metrics": true,
"log_level": "INFO",
"log_to_cloud": true,
"collection_configuration": {
"collect_workload_validation_metrics": true
},
"discovery_configuration": {
"enable_discovery": true
}
}
9 changes: 9 additions & 0 deletions internal/startdaemon/test_data/overwritten_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"provide_sap_host_agent_metrics": true,
"log_level": "DEBUG",
"log_to_cloud": true,
"collection_configuration": {
"collect_workload_validation_metrics": false,
"collect_process_metrics": true
}
}