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

[POA-1513] Add support for different agent install paths in EC2 add #28

Merged
merged 4 commits into from
Aug 12, 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
89 changes: 52 additions & 37 deletions cmd/internal/ec2/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,26 @@ import (
"os/exec"
"os/user"
"strings"
"text/template"

"github.com/AlecAivazis/survey/v2"
"github.com/pkg/errors"
"github.com/postmanlabs/postman-insights-agent/cfg"
"github.com/postmanlabs/postman-insights-agent/consts"
"github.com/postmanlabs/postman-insights-agent/printer"
"github.com/postmanlabs/postman-insights-agent/telemetry"
"github.com/postmanlabs/postman-insights-agent/util"
)

const (
envFileName = "postman-insights-agent"
envFileTemplateName = "postman-insights-agent.tmpl"
envFileTemplateName = "postman-insights-agent.env.tmpl"
envFileBasePath = "/etc/default/"
envFilePath = envFileBasePath + envFileName

serviceFileName = "postman-insights-agent.service"
serviceFileBasePath = "/usr/lib/systemd/system/"
serviceFilePath = serviceFileBasePath + serviceFileName
serviceFileName = "postman-insights-agent.service"
serviceFileTemplateName = "postman-insights-agent.service.tmpl"
serviceFileBasePath = "/usr/lib/systemd/system/"
serviceFilePath = serviceFileBasePath + serviceFileName

// Output of command: systemctl is-enabled postman-insights-agent
// Refer: https://www.freedesktop.org/software/systemd/man/latest/systemctl.html#Exit%20status
Expand All @@ -33,15 +34,30 @@ const (
nonExisting = "Failed to get unit file state for postman-insights-agent.service: No such file or directory" // exit code: 1
)

// Embed files inside the binary. Requires Go >=1.16
var (
agentInstallPaths = [...]string{
// Agent executable name
"postman-insights-agent",

//go:embed postman-insights-agent.service
var serviceFile string
// If agent is not found in directories named by PATH environment variable then look for below predefined paths

// FS is used for easier template parsing
// Debian install path
"/usr/bin/postman-insights-agent",
// Homebrew install path
"/opt/homebrew/bin/postman-insights-agent",
// Usr local install path
"/usr/local/bin/postman-insights-agent",
}

// Embed files inside the binary. Requires Go >=1.16
// FS is used for easier template parsing

//go:embed postman-insights-agent.env.tmpl
envFileFS embed.FS

//go:embed postman-insights-agent.tmpl
var envFileFS embed.FS
//go:embed postman-insights-agent.service.tmpl
serviceFileFS embed.FS
)

// Helper function for reporting telemetry
func reportStep(stepName string) {
Expand Down Expand Up @@ -162,6 +178,20 @@ func checkSystemdExists() error {
return nil
}

func getAgentInstallPath() (string, error) {
message := "Checking agent install path "
printer.Infof(message + "\n")
reportStep(message)

for _, possiblePath := range agentInstallPaths {
if path, err := exec.LookPath(possiblePath); err == nil {
return path, nil
}
}

return "", errors.Errorf("Could not find postman-insights-agent binary in your OS.")
}

func configureSystemdFiles(projectID string) error {
message := "Configuring systemd files"
printer.Infof(message + "\n")
Expand All @@ -172,14 +202,9 @@ func configureSystemdFiles(projectID string) error {
return err
}

// Write projectID and Postman API Key to go template file
tmpl, err := template.ParseFS(envFileFS, envFileTemplateName)
if err != nil {
return errors.Wrapf(err, "systemd env file parsing failed")
}

// -------- Write env file --------
apiKey, env := cfg.GetPostmanAPIKeyAndEnvironment()
data := struct {
envFiledata := struct {
PostmanEnv string
PostmanAPIKey string
ProjectID string
Expand All @@ -188,38 +213,28 @@ func configureSystemdFiles(projectID string) error {
ProjectID: projectID,
}
if env != "" {
data.PostmanEnv = env
envFiledata.PostmanEnv = env
}

// Ensure /etc/default exists
cmd := exec.Command("mkdir", []string{"-p", envFileBasePath}...)
_, err = cmd.CombinedOutput()
err = util.GenerateAndWriteTemplateFile(envFileFS, envFileTemplateName, envFileBasePath, envFileName, envFiledata)
if err != nil {
return errors.Wrapf(err, "failed to create %s directory\n", envFileBasePath)
}

envFile, err := os.Create(envFilePath)
if err != nil {
printer.Errorf("Failed to create systemd env file")
return err
}

err = tmpl.Execute(envFile, data)
// -------- Write service file --------
agentInstallPath, err := getAgentInstallPath()
if err != nil {
printer.Errorf("Failed to write values to systemd env file")
return err
}

// Ensure /usr/lib/systemd/system exists
cmd = exec.Command("mkdir", []string{"-p", serviceFileBasePath}...)
_, err = cmd.CombinedOutput()
if err != nil {
return errors.Wrapf(err, "failed to create %s directory", serviceFileBasePath)
serviceFileData := struct {
AgentInstallPath string
}{
AgentInstallPath: agentInstallPath,
}

err = os.WriteFile(serviceFilePath, []byte(serviceFile), 0600)
err = util.GenerateAndWriteTemplateFile(serviceFileFS, serviceFileTemplateName, serviceFileBasePath, serviceFileName, serviceFileData)
if err != nil {
printer.Errorf("failed to create %s file in %s directory with err %q \n", serviceFileName, serviceFilePath, err)
return err
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ EnvironmentFile=/etc/default/postman-insights-agent
# DO NOT CHANGE
# "${FOO}" uses the arguement as is, while "$FOO" splits the string on white space
# Reference: https://www.freedesktop.org/software/systemd/man/systemd.service.html#Command%20lines
ExecStart=/usr/bin/postman-insights-agent apidump --project "${PROJECT_ID}" --interfaces "${INTERFACES}" --filter "${FILTER}" "$EXTRA_APIDUMP_ARGS"
ExecStart={{.AgentInstallPath}} apidump --project "${PROJECT_ID}" --interfaces "${INTERFACES}" --filter "${FILTER}" "$EXTRA_APIDUMP_ARGS"

[Install]
WantedBy=multi-user.target
44 changes: 44 additions & 0 deletions util/templateUtils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package util

import (
"embed"
"os"
"os/exec"
"text/template"

"github.com/pkg/errors"
)

func GenerateAndWriteTemplateFile(
fileFS embed.FS,
templateName string,
fileDirectory string,
fileName string,
data interface{},
) error {
// Parse the template file
tmpl, err := template.ParseFS(fileFS, templateName)
if err != nil {
return errors.Wrapf(err, "Failed to parse %s file\n", fileName)
}

// Ensure directory exists
cmd := exec.Command("mkdir", []string{"-p", fileDirectory}...)
_, err = cmd.CombinedOutput()
if err != nil {
return errors.Wrapf(err, "Failed to create %s directory\n", fileDirectory)
}

// Create the file
file, err := os.Create(fileDirectory + fileName)
if err != nil {
return errors.Wrapf(err, "Failed to create %s file in %s directory\n", fileName, fileDirectory)
}

// Write the data to the file
err = tmpl.Execute(file, data)
if err != nil {
return errors.Wrapf(err, "Failed to write values to %s file\n", fileName)
}
return nil
}