Skip to content

Commit

Permalink
Added config validation and generation logic
Browse files Browse the repository at this point in the history
  • Loading branch information
XuechunHou committed Jan 17, 2025
1 parent 19231ae commit 710317e
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 1 deletion.
6 changes: 6 additions & 0 deletions cmd/ops_agent_uap_plugin/plugin.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"flag"
"fmt"
"net"
Expand Down Expand Up @@ -53,6 +54,11 @@ func main() {
// offered mean Guest Agent was successful in installing/launching the plugin
// & will manage the lifecycle (start, stop, or revision change) here onwards.
pb.RegisterGuestAgentPluginServer(server, ps)

ctx := context.Background()
ps.GetStatus(ctx, &pb.GetStatusRequest{})
ps.Start(ctx, &pb.StartRequest{})

if err := server.Serve(listener); err != nil {
fmt.Fprintf(os.Stderr, "Exiting, cannot continue serving: %v\n", err)
os.Exit(1)
Expand Down
82 changes: 81 additions & 1 deletion cmd/ops_agent_uap_plugin/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,27 @@ package main

import (
"context"
"fmt"
"log"
"os/exec"
"syscall"

"google.golang.org/grpc"
"google.golang.org/grpc/status"

pb "github.com/GoogleCloudPlatform/ops-agent/cmd/ops_agent_uap_plugin/google_guest_agent/plugin"
)

const (
OpsAgentConfigLocationLinux = "/etc/google-cloud-ops-agent/config.yaml"
ConfGeneratorBinary = "libexec/google_cloud_ops_agent_engine"
LogsDirectory = "log/google-cloud-ops-agent"
FluentBitStateDiectory = "state/fluent-bit"
FluentBitRuntimeDirectory = "run/google-cloud-ops-agent-fluent-bit"
OtelRuntimeDirectory = "run/google-cloud-ops-agent-opentelemetry-collector"
DefaultPluginStateDirectory = "/var/lib/google-guest-agent/plugins/ops-agent-plugin"
)

// PluginServer implements the plugin RPC server interface.
type OpsAgentPluginServer struct {
pb.UnimplementedGuestAgentPluginServer
Expand All @@ -35,8 +49,24 @@ func (ps *OpsAgentPluginServer) Start(ctx context.Context, msg *pb.StartRequest)
}
log.Printf("Received a Start request: %s. Starting the Ops Agent", msg)

_, cancel := context.WithCancel(context.Background())
pContext, cancel := context.WithCancel(context.Background())
ps.cancel = cancel

pluginStateDir := msg.GetConfig().GetStateDirectoryPath()
if pluginStateDir == "" {
pluginStateDir = DefaultPluginStateDirectory
}
// Ops Agent config validation
if err := validateOpsAgentConfig(pContext, pluginStateDir); err != nil {
log.Printf("failed to validate Ops Agent config: %s", err)
return nil, status.Errorf(1, "failed to validate Ops Agent config: %s", err)
}
// Subagent config generation
if err := generateSubagentConfigs(pContext, pluginStateDir); err != nil {
log.Printf("failed to generate subagent configs: %s", err)
return nil, status.Errorf(1, "failed to generate subagent configs: %s", err)
}

return &pb.StartResponse{}, nil
}

Expand Down Expand Up @@ -69,3 +99,53 @@ func (ps *OpsAgentPluginServer) GetStatus(ctx context.Context, msg *pb.GetStatus
log.Println("The Ops Agent plugin is running")
return &pb.Status{Code: 0, Results: []string{"The Ops Agent Plugin is running ok."}}, nil
}

func runCommand(cmd *exec.Cmd) error {
if cmd == nil {
return nil
}
cmd.SysProcAttr = &syscall.SysProcAttr{
Pdeathsig: syscall.SIGKILL,

Check failure on line 108 in cmd/ops_agent_uap_plugin/service.go

View workflow job for this annotation

GitHub Actions / test (windows-latest)

unknown field Pdeathsig in struct literal of type "syscall".SysProcAttr

Check failure on line 108 in cmd/ops_agent_uap_plugin/service.go

View workflow job for this annotation

GitHub Actions / test (windows-latest)

unknown field Pdeathsig in struct literal of type "syscall".SysProcAttr

Check failure on line 108 in cmd/ops_agent_uap_plugin/service.go

View workflow job for this annotation

GitHub Actions / test (windows-latest)

unknown field Pdeathsig in struct literal of type "syscall".SysProcAttr

Check failure on line 108 in cmd/ops_agent_uap_plugin/service.go

View workflow job for this annotation

GitHub Actions / test (windows-latest)

unknown field Pdeathsig in struct literal of type "syscall".SysProcAttr
}
log.Printf("Running command: %s, with arguments: %s", cmd.Path, cmd.Args)
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to execute cmd: %s with arguments %s, \ncommand output: %s\ncommand error: %s", cmd.Path, cmd.Args, out, err)
}
return nil
}

func validateOpsAgentConfig(ctx context.Context, pluginBaseLocation string) error {
configValidationCmd := exec.CommandContext(ctx,
pluginBaseLocation+"/"+ConfGeneratorBinary,
"-in", OpsAgentConfigLocationLinux,
)
if err := runCommand(configValidationCmd); err != nil {
return fmt.Errorf("failed to validate the Ops Agent config: %s", err)
}
return nil
}

func generateSubagentConfigs(ctx context.Context, pluginBaseLocation string) error {
otelConfigGenerationCmd := exec.CommandContext(ctx,
pluginBaseLocation+"/"+ConfGeneratorBinary,
"-service", "otel",
"-in", OpsAgentConfigLocationLinux,
"-out", pluginBaseLocation+"/"+OtelRuntimeDirectory,
"-logs", pluginBaseLocation+"/"+LogsDirectory)

if err := runCommand(otelConfigGenerationCmd); err != nil {
return fmt.Errorf("failed to generate Otel config: %s", err)
}

fluentBitConfigGenerationCmd := exec.CommandContext(ctx,
pluginBaseLocation+"/libexec/google_cloud_ops_agent_engine",
"-service", "fluentbit",
"-in", OpsAgentConfigLocationLinux,
"-out", pluginBaseLocation+"/"+FluentBitRuntimeDirectory,
"-logs", pluginBaseLocation+"/"+LogsDirectory, "-state", pluginBaseLocation+"/"+FluentBitStateDiectory)

if err := runCommand(fluentBitConfigGenerationCmd); err != nil {
return fmt.Errorf("failed to generate Fluntbit config: %s", err)
}
return nil
}
54 changes: 54 additions & 0 deletions cmd/ops_agent_uap_plugin/service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2024 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 (
"os"
"os/exec"
"testing"
)

func TestRunCommand(t *testing.T) {
cmd := exec.Command(os.Args[0], "-test.run=TestHelperProcess")
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
err := runCommand(cmd)
if err != nil {
t.Errorf("runCommand got unexpected error: %v", err)
}
}
func TestRunCommandFailure(t *testing.T) {
cmd := exec.Command(os.Args[0], "-test.run=TestHelperProcess")
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1", "GO_HELPER_FAILURE=1"}
if err := runCommand(cmd); err == nil {
t.Error("runCommand got nil error, want exec failure")
}
}

// TestHelperProcess isn't a real test. It's used as a helper process to mock
// command executions.
func TestHelperProcess(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
// Skip this test if it's not invoked explicitly as a helper
// process. return allows the next tests to continue running.
return
}
switch {
case os.Getenv("GO_HELPER_FAILURE") == "1":
os.Exit(1)
default:
// A "successful" mock execution exits with a successful (zero) exit code.
os.Exit(0)
}
}

0 comments on commit 710317e

Please sign in to comment.