-
Notifications
You must be signed in to change notification settings - Fork 73
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
Config Management via UAP #1919
Merged
Merged
Changes from all commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
51aecb7
write received custom config to config.yaml file
XuechunHou 73826fd
when input config is empty, dont overwrite the config.yaml file
XuechunHou a5b3962
final impl
XuechunHou 0283197
added an integration test
XuechunHou ec41a64
do not reuse the same file name
XuechunHou b5a092c
fixed tests
XuechunHou 4e15443
fixed typo
XuechunHou 019414b
test string config received from UAp
XuechunHou 7715493
test invalid struct config
XuechunHou 672ddb4
added additional tests
XuechunHou 9eb60d6
fixed string formatting
XuechunHou ad6a184
fixed typo
XuechunHou 5aee724
stringify yaml
XuechunHou 0148e06
removed unecessary test
XuechunHou ee0b203
fixed yaml strigify
XuechunHou 0cbb3b7
stringify struct proto
XuechunHou b960062
fixed typo
XuechunHou 8f6d743
wrap string with double quote
XuechunHou 2db100c
fixed typo
XuechunHou 6aa3454
added additional unit tests
XuechunHou 59f6b81
removed unnecessary struct fields
XuechunHou eb76e7f
fixed typo
XuechunHou adade48
removed comments
XuechunHou 6511e2e
escape double quote
XuechunHou 7ceff27
removed escaping
XuechunHou 85d125e
removed tests
XuechunHou 23f0d74
updated test
XuechunHou f530e23
simplify invalid config input
XuechunHou 4701faf
skip windows
XuechunHou d399170
updated unit tests
XuechunHou dff9d50
updated docker template
XuechunHou File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
// Copyright 2025 Google LLC | ||
// | ||
// 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 main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"buf.build/go/protoyaml" // Import the protoyaml-go package | ||
"github.com/GoogleCloudPlatform/ops-agent/apps" | ||
"github.com/GoogleCloudPlatform/ops-agent/confgenerator" | ||
"github.com/GoogleCloudPlatform/ops-agent/internal/platform" | ||
|
||
pb "github.com/GoogleCloudPlatform/ops-agent/cmd/ops_agent_uap_plugin/google_guest_agent/plugin" | ||
spb "google.golang.org/protobuf/types/known/structpb" | ||
) | ||
|
||
func customLogPathByOsType(ctx context.Context) string { | ||
osType := platform.FromContext(ctx).Name() | ||
if osType == "linux" { | ||
return "/var/log" | ||
} | ||
return `C:\mylog` | ||
} | ||
func TestWriteCustomConfigToFile(t *testing.T) { | ||
yamlConfig := fmt.Sprintf(`logging: | ||
receivers: | ||
mylog_source: | ||
type: files | ||
include_paths: | ||
- %s | ||
exporters: | ||
google: | ||
type: google_cloud_logging | ||
processors: | ||
my_exclude: | ||
type: exclude_logs | ||
match_any: | ||
- jsonPayload.missing_field = "value" | ||
- jsonPayload.message =~ "test pattern" | ||
service: | ||
pipelines: | ||
my_pipeline: | ||
receivers: [mylog_source] | ||
processors: [my_exclude] | ||
exporters: [google]`, customLogPathByOsType(context.Background())) | ||
structConfig := &spb.Struct{} | ||
err := protoyaml.Unmarshal([]byte(yamlConfig), structConfig) | ||
if err != nil { | ||
t.Fatalf("Failed to unmarshal YAML into structpb.Struct: %v", err) | ||
} | ||
|
||
tests := []struct { | ||
name string | ||
req *pb.StartRequest | ||
}{ | ||
{ | ||
name: "Received a valid StringConfig from UAP, the output should be a valid Ops agent yaml", | ||
req: &pb.StartRequest{ | ||
ServiceConfig: &pb.StartRequest_StringConfig{ | ||
StringConfig: yamlConfig, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "Received a valid StructConfig from UAP, the output should be a valid Ops agent yaml", | ||
req: &pb.StartRequest{ | ||
ServiceConfig: &pb.StartRequest_StructConfig{ | ||
StructConfig: structConfig, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tc := range tests { | ||
t.Run(tc.name, func(t *testing.T) { | ||
// Create a temporary directory for the test file | ||
tmpDir := t.TempDir() | ||
configPath := filepath.Join(tmpDir, "ops-agent-config", fmt.Sprintf("%sconfig.yaml", tc.name)) | ||
|
||
err := writeCustomConfigToFile(tc.req, configPath) | ||
|
||
if err != nil { | ||
t.Errorf("%v: writeCustomConfigToFile got error: %v, want nil error", tc.name, err) | ||
} | ||
|
||
_, err = confgenerator.MergeConfFiles(context.Background(), configPath, apps.BuiltInConfStructs) | ||
if err != nil { | ||
t.Errorf("%v: conf generator fails to validate the output Ops agent yaml: %v", tc.name, err) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestWriteCustomConfigToFile_receivedEmptyCustomConfig(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
req *pb.StartRequest | ||
}{ | ||
{ | ||
name: "The ops agent config.yaml file should not be modified if UAP does not send any StringConfig", | ||
req: &pb.StartRequest{}, | ||
}, | ||
{ | ||
name: "The ops agent config.yaml file should not be modified if UAP does not send any StructConfig", | ||
req: &pb.StartRequest{}, | ||
}, | ||
} | ||
|
||
for _, tc := range tests { | ||
t.Run(tc.name, func(t *testing.T) { | ||
configFile, err := os.CreateTemp("", "config.yaml") | ||
if err != nil { | ||
t.Fatalf("%v: failed to create the config.yaml file at location: %s, error: %v", tc.name, configFile.Name(), err) | ||
} | ||
configPath := configFile.Name() | ||
wantFileContent := "1234" | ||
if _, err := configFile.WriteString(wantFileContent); err != nil { | ||
t.Fatalf("%v: failed to write to the config.yaml file at location: %s, error: %v", tc.name, configPath, err) | ||
} | ||
|
||
err = writeCustomConfigToFile(tc.req, configPath) | ||
if err != nil { | ||
t.Errorf("%v: writeCustomConfigToFile got error: %v, want nil error", tc.name, err) | ||
} | ||
|
||
gotContent, err := os.ReadFile(configPath) | ||
if err != nil { | ||
t.Fatalf("%s: failed to read the config.yaml file content: %v", tc.name, err) | ||
} | ||
if string(gotContent) != wantFileContent { | ||
t.Errorf("%s: got config.yaml content: %v, want: %v", tc.name, string(gotContent), wantFileContent) | ||
} | ||
configFile.Close() | ||
os.Remove(configPath) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a user inputs a custom yaml and it's able to parse into a
StructConfig
, would theStringConfig
also be sent, or only the parsed version?If both are sent, wouldn't this case never trigger?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, do we even want to use the parsed config?
Since we simply write the config to a file and let the Ops Agent handle it, do we need to check for proper yaml at this stage?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://source.corp.google.com/piper///depot/google3/third_party/guest_agent/dev/internal/plugin/proto/plugin_comm.proto;l=53
service_config
is a oneof field, so either string_config or struct_config is sent perStartRequest
, never both.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Also, do we even want to use the parsed config?" If the FE allows both, we will have to handle both.
struct_config is populated when users upload a config.yaml file at the FE, it will be parsed, validated and converted to a struct proto at the FE before us receiving it.
string_config is populated when users paste a chunk of string at the FE, the string will be forwarded to us as is without any validation.
I am not doing any validation at this stage really, I am just converting the struct proto back to a yaml and writes it to config.yaml file. Whether it's a valid Ops Agent config yaml is left to the conf generator...