Skip to content
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
4 changes: 3 additions & 1 deletion integration-test/env/Extension/HandlerEnvironment.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"logFolder": "/var/log/azure/Extension",
"configFolder": "/var/lib/waagent/Extension/config",
"statusFolder": "/var/lib/waagent/Extension/status",
"heartbeatFile": "/var/lib/waagent/Extension/heartbeat.log"
"heartbeatFile": "/var/lib/waagent/Extension/heartbeat.log",
"eventsFolder": "/var/log/azure/Extension/events",
"eventsFolder_preview": "/var/log/azure/Extension/events"
}
}
]
6 changes: 3 additions & 3 deletions integration-test/test/7_vmwatch.bats
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ teardown(){
[[ "$output" == *'VMWatch process started'* ]]
[[ "$output" == *'--config /var/lib/waagent/Extension/bin/VMWatch/vmwatch.conf'* ]]
[[ "$output" == *'--input-filter disk_io:outbound_connectivity:clockskew:az_storage_blob'* ]]
[[ "$output" == *'Env: [SIGNAL_FOLDER=/var/log/azure/Microsoft.ManagedServices.ApplicationHealthLinux/events VERBOSE_LOG_FILE_FULL_PATH=/var/log/azure/Extension/vmwatch.log]'* ]]
[[ "$output" == *'Env: [SIGNAL_FOLDER=/var/log/azure/Extension/events VERBOSE_LOG_FILE_FULL_PATH=/var/log/azure/Extension/vmwatch.log]'* ]]
[[ "$output" == *'VMWatch is running'* ]]

status_file="$(container_read_extension_status)"
Expand Down Expand Up @@ -131,7 +131,7 @@ teardown(){
[[ "$output" == *'VMWatch process started'* ]]
[[ "$output" == *'--config /var/lib/waagent/Extension/bin/VMWatch/vmwatch.conf'* ]]
[[ "$output" == *'--input-filter disk_io:outbound_connectivity'* ]]
[[ "$output" == *'Env: [ABC=abc BCD=bcd SIGNAL_FOLDER=/var/log/azure/Microsoft.ManagedServices.ApplicationHealthLinux/events VERBOSE_LOG_FILE_FULL_PATH=/var/log/azure/Extension/vmwatch.log]'* ]]
[[ "$output" == *'Env: [ABC=abc BCD=bcd SIGNAL_FOLDER=/var/log/azure/Extension/events VERBOSE_LOG_FILE_FULL_PATH=/var/log/azure/Extension/vmwatch.log]'* ]]
[[ "$output" == *'VMWatch is running'* ]]

status_file="$(container_read_extension_status)"
Expand Down Expand Up @@ -174,7 +174,7 @@ teardown(){
[[ "$output" == *'VMWatch process started'* ]]
[[ "$output" == *'--config /var/lib/waagent/Extension/bin/VMWatch/vmwatch.conf'* ]]
[[ "$output" == *'--input-filter disk_io:outbound_connectivity'* ]]
[[ "$output" == *'Env: [SIGNAL_FOLDER=/var/log/azure/Microsoft.ManagedServices.ApplicationHealthLinux/events VERBOSE_LOG_FILE_FULL_PATH=/var/log/azure/Extension/vmwatch.log]'* ]]
[[ "$output" == *'Env: [SIGNAL_FOLDER=/var/log/azure/Extension/events VERBOSE_LOG_FILE_FULL_PATH=/var/log/azure/Extension/vmwatch.log]'* ]]
[[ "$output" == *'VMWatch is running'* ]]

status_file="$(container_read_extension_status)"
Expand Down
16 changes: 8 additions & 8 deletions main/cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ package main

import (
"fmt"
"github.com/Azure/azure-docker-extension/pkg/vmextension"
"github.com/go-kit/kit/log"
"github.com/pkg/errors"
"os"
"strings"
"time"

"github.com/go-kit/kit/log"
"github.com/pkg/errors"
)

type cmdFunc func(ctx *log.Context, hEnv vmextension.HandlerEnvironment, seqNum int) (msg string, err error)
type cmdFunc func(ctx *log.Context, hEnv HandlerEnvironment, seqNum int) (msg string, err error)
type preFunc func(ctx *log.Context, seqNum int) error

type cmd struct {
Expand Down Expand Up @@ -39,12 +39,12 @@ var (
}
)

func noop(ctx *log.Context, h vmextension.HandlerEnvironment, seqNum int) (string, error) {
func noop(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, error) {
ctx.Log("event", "noop")
return "", nil
}

func install(ctx *log.Context, h vmextension.HandlerEnvironment, seqNum int) (string, error) {
func install(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, error) {
if err := os.MkdirAll(dataDir, 0755); err != nil {
return "", errors.Wrap(err, "failed to create data dir")
}
Expand All @@ -54,7 +54,7 @@ func install(ctx *log.Context, h vmextension.HandlerEnvironment, seqNum int) (st
return "", nil
}

func uninstall(ctx *log.Context, h vmextension.HandlerEnvironment, seqNum int) (string, error) {
func uninstall(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, error) {
{ // a new context scope with path
ctx = ctx.With("path", dataDir)
ctx.Log("event", "removing data dir", "path", dataDir)
Expand All @@ -75,7 +75,7 @@ var (
errTerminated = errors.New("Application health process terminated")
)

func enable(ctx *log.Context, h vmextension.HandlerEnvironment, seqNum int) (string, error) {
func enable(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, error) {
// parse the extension handler settings (not available prior to 'enable')
cfg, err := parseAndValidateSettings(ctx, h.HandlerEnvironment.ConfigFolder)
if err != nil {
Expand Down
1 change: 0 additions & 1 deletion main/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ const (
// and it also doesn't have the latest properties like EventsFolder. Importing a separate package
// is possible, but may result in lots of code churn. We will temporarily keep this as a constant since the
// events folder is unlikely to change in the future.
HandlerEnvironmentEventsFolderPath = "/var/log/azure/Microsoft.ManagedServices.ApplicationHealthLinux/events"

VMWatchBinaryNameAmd64 = "vmwatch_linux_amd64"
VMWatchBinaryNameArm64 = "vmwatch_linux_arm64"
Expand Down
84 changes: 84 additions & 0 deletions main/handlerenv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)

// HandlerEnvFileName is the file name of the Handler Environment as placed by the
// Azure Linux Guest Agent.
const HandlerEnvFileName = "HandlerEnvironment.json"

// HandlerEnvironment describes the handler environment configuration presented
// to the extension handler by the Azure Linux Guest Agent.
type HandlerEnvironment struct {
Version float64 `json:"version"`
Name string `json:"name"`
HandlerEnvironment struct {
HeartbeatFile string `json:"heartbeatFile"`
StatusFolder string `json:"statusFolder"`
ConfigFolder string `json:"configFolder"`
LogFolder string `json:"logFolder"`
EventsFolder string `json:"eventsFolder"`
EventsFolderPreview string `json:"eventsFolder_preview"`
DeploymentID string `json:"deploymentid"`
RoleName string `json:"rolename"`
Instance string `json:"instance"`
HostResolverAddress string `json:"hostResolverAddress"`
}
}

// GetHandlerEnv locates the HandlerEnvironment.json file by assuming it lives
// next to or one level above the extension handler (read: this) executable,
// reads, parses and returns it.
func GetHandlerEnv() (he HandlerEnvironment, _ error) {
dir, err := scriptDir()
if err != nil {
return he, fmt.Errorf("vmextension: cannot find base directory of the running process: %v", err)
}
paths := []string{
filepath.Join(dir, HandlerEnvFileName), // this level (i.e. executable is in [EXT_NAME]/.)
filepath.Join(dir, "..", HandlerEnvFileName), // one up (i.e. executable is in [EXT_NAME]/bin/.)
}
var b []byte
for _, p := range paths {
o, err := ioutil.ReadFile(p)
if err != nil && !os.IsNotExist(err) {
return he, fmt.Errorf("vmextension: error examining HandlerEnvironment at '%s': %v", p, err)
} else if err == nil {
b = o
break
}
}
if b == nil {
return he, fmt.Errorf("vmextension: Cannot find HandlerEnvironment at paths: %s", strings.Join(paths, ", "))
}
return ParseHandlerEnv(b)
}

// scriptDir returns the absolute path of the running process.
func scriptDir() (string, error) {
p, err := filepath.Abs(os.Args[0])
if err != nil {
return "", err
}
return filepath.Dir(p), nil
}

// ParseHandlerEnv parses the
// /var/lib/waagent/[extension]/HandlerEnvironment.json format.
func ParseHandlerEnv(b []byte) (he HandlerEnvironment, _ error) {
var hf []HandlerEnvironment

if err := json.Unmarshal(b, &hf); err != nil {
return he, fmt.Errorf("vmextension: failed to parse handler env: %v", err)
}
if len(hf) != 1 {
return he, fmt.Errorf("vmextension: expected 1 config in parsed HandlerEnvironment, found: %v", len(hf))
}
return hf[0], nil
}
5 changes: 2 additions & 3 deletions main/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"strings"
"syscall"

"github.com/Azure/azure-docker-extension/pkg/vmextension"
"github.com/go-kit/kit/log"
)

Expand Down Expand Up @@ -42,12 +41,12 @@ func main() {
}()

// parse extension environment
hEnv, err := vmextension.GetHandlerEnv()
hEnv, err := GetHandlerEnv()
if err != nil {
ctx.Log("message", "failed to parse handlerenv", "error", err)
os.Exit(cmd.failExitCode)
}
seqNum, err := vmextension.FindSeqNum(hEnv.HandlerEnvironment.ConfigFolder)
seqNum, err := FindSeqNum(hEnv.HandlerEnvironment.ConfigFolder)
if err != nil {
ctx.Log("messsage", "failed to find sequence number", "error", err)
}
Expand Down
5 changes: 2 additions & 3 deletions main/reportstatus.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"github.com/Azure/azure-docker-extension/pkg/vmextension"
"github.com/go-kit/kit/log"
"github.com/pkg/errors"
)
Expand All @@ -11,7 +10,7 @@ import (
// status.
//
// If an error occurs reporting the status, it will be logged and returned.
func reportStatus(ctx *log.Context, hEnv vmextension.HandlerEnvironment, seqNum int, t StatusType, c cmd, msg string) error {
func reportStatus(ctx *log.Context, hEnv HandlerEnvironment, seqNum int, t StatusType, c cmd, msg string) error {
if !c.shouldReportStatus {
ctx.Log("status", "not reported for operation (by design)")
return nil
Expand All @@ -24,7 +23,7 @@ func reportStatus(ctx *log.Context, hEnv vmextension.HandlerEnvironment, seqNum
return nil
}

func reportStatusWithSubstatuses(ctx *log.Context, hEnv vmextension.HandlerEnvironment, seqNum int, t StatusType, op string, msg string, substatuses []SubstatusItem) error {
func reportStatusWithSubstatuses(ctx *log.Context, hEnv HandlerEnvironment, seqNum int, t StatusType, op string, msg string, substatuses []SubstatusItem) error {
s := NewStatus(t, op, msg)
for _, substatus := range substatuses {
s.AddSubstatusItem(substatus)
Expand Down
7 changes: 3 additions & 4 deletions main/reportstatus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"path/filepath"
"testing"

"github.com/Azure/azure-docker-extension/pkg/vmextension"
"github.com/go-kit/kit/log"
"github.com/stretchr/testify/require"
)
Expand All @@ -23,7 +22,7 @@ func Test_statusMsg(t *testing.T) {
}

func Test_reportStatus_fails(t *testing.T) {
fakeEnv := vmextension.HandlerEnvironment{}
fakeEnv := HandlerEnvironment{}
fakeEnv.HandlerEnvironment.StatusFolder = "/non-existing/dir/"

err := reportStatus(log.NewContext(log.NewNopLogger()), fakeEnv, 1, StatusSuccess, cmdEnable, "")
Expand All @@ -36,7 +35,7 @@ func Test_reportStatus_fileExists(t *testing.T) {
require.Nil(t, err)
defer os.RemoveAll(tmpDir)

fakeEnv := vmextension.HandlerEnvironment{}
fakeEnv := HandlerEnvironment{}
fakeEnv.HandlerEnvironment.StatusFolder = tmpDir

require.Nil(t, reportStatus(log.NewContext(log.NewNopLogger()), fakeEnv, 1, StatusError, cmdEnable, "FOO ERROR"))
Expand All @@ -53,7 +52,7 @@ func Test_reportStatus_checksIfShouldBeReported(t *testing.T) {
require.Nil(t, err)
defer os.RemoveAll(tmpDir)

fakeEnv := vmextension.HandlerEnvironment{}
fakeEnv := HandlerEnvironment{}
fakeEnv.HandlerEnvironment.StatusFolder = tmpDir
require.Nil(t, reportStatus(log.NewContext(log.NewNopLogger()), fakeEnv, 2, StatusSuccess, c, ""))

Expand Down
32 changes: 32 additions & 0 deletions main/seqnum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package main

import (
"fmt"
"path/filepath"
"sort"
"strconv"
"strings"
)

// FindSeqnum finds the file with the highest number under configFolder
// named like 0.settings, 1.settings so on.
func FindSeqNum(configFolder string) (int, error) {
g, err := filepath.Glob(configFolder + "/*.settings")
if err != nil {
return 0, err
}
seqs := make([]int, len(g))
for _, v := range g {
f := filepath.Base(v)
i, err := strconv.Atoi(strings.Replace(f, ".settings", "", 1))
if err != nil {
return 0, fmt.Errorf("Can't parse int from filename: %s", f)
}
seqs = append(seqs, i)
}
if len(seqs) == 0 {
return 0, fmt.Errorf("Can't find out seqnum from %s, not enough files.", configFolder)
}
sort.Sort(sort.Reverse(sort.IntSlice(seqs)))
return seqs[0], nil
}
24 changes: 12 additions & 12 deletions main/vmWatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package main
import (
"bytes"
"fmt"
"github.com/Azure/azure-docker-extension/pkg/vmextension"
"github.com/go-kit/kit/log"
"os"
"os/exec"
"path/filepath"
"strings"
"time"

"github.com/go-kit/kit/log"
)

type VMWatchStatus string
Expand Down Expand Up @@ -49,7 +49,7 @@ func (r *VMWatchResult) GetMessage() string {

// We will setup and execute VMWatch as a separate process. Ideally VMWatch should run indefinitely,
// but as a best effort we will attempt at most 3 times to run the process
func executeVMWatch(ctx *log.Context, s *vmWatchSettings, h vmextension.HandlerEnvironment, vmWatchResultChannel chan VMWatchResult) {
func executeVMWatch(ctx *log.Context, s *vmWatchSettings, hEnv HandlerEnvironment, vmWatchResultChannel chan VMWatchResult) {
var vmWatchErr error
defer func() {
if r := recover(); r != nil {
Expand All @@ -63,11 +63,11 @@ func executeVMWatch(ctx *log.Context, s *vmWatchSettings, h vmextension.HandlerE

// Best effort to start VMWatch process each time it fails
for i := 1; i <= VMWatchMaxProcessAttempts; i++ {
vmWatchErr = executeVMWatchHelper(ctx, i, s, h)
vmWatchErr = executeVMWatchHelper(ctx, i, s, hEnv)
}
}

func executeVMWatchHelper(ctx *log.Context, attempt int, vmWatchSettings *vmWatchSettings, handlerEnvironment vmextension.HandlerEnvironment) (err error) {
func executeVMWatchHelper(ctx *log.Context, attempt int, vmWatchSettings *vmWatchSettings, hEnv HandlerEnvironment) (err error) {
pid := -1
var cmd *exec.Cmd
defer func() {
Expand All @@ -79,9 +79,9 @@ func executeVMWatchHelper(ctx *log.Context, attempt int, vmWatchSettings *vmWatc
}()

// Setup command
cmd, err = setupVMWatchCommand(vmWatchSettings, handlerEnvironment)
cmd, err = setupVMWatchCommand(vmWatchSettings, hEnv)
if err != nil {
err = fmt.Errorf("[%v][PID -1] Attempt %d: VMWatch setup failed. Error: %w", time.Now().UTC().Format(time.RFC3339), pid, attempt, err)
err = fmt.Errorf("[%v][PID -1] Attempt %d: VMWatch setup failed. Error: %w", time.Now().UTC().Format(time.RFC3339), attempt, err)
ctx.Log("error", err.Error())
return err
}
Expand Down Expand Up @@ -125,7 +125,7 @@ func killVMWatch(ctx *log.Context, cmd *exec.Cmd) error {
return nil
}

func setupVMWatchCommand(s *vmWatchSettings, h vmextension.HandlerEnvironment) (*exec.Cmd, error) {
func setupVMWatchCommand(s *vmWatchSettings, hEnv HandlerEnvironment) (*exec.Cmd, error) {
processDirectory, err := GetProcessDirectory()
if err != nil {
return nil, err
Expand All @@ -142,7 +142,7 @@ func setupVMWatchCommand(s *vmWatchSettings, h vmextension.HandlerEnvironment) (

cmd := exec.Command(GetVMWatchBinaryFullPath(processDirectory), args...)

cmd.Env = GetVMWatchEnvironmentVariables(s.ParameterOverrides, h)
cmd.Env = GetVMWatchEnvironmentVariables(s.ParameterOverrides, hEnv)

return cmd, nil
}
Expand All @@ -169,14 +169,14 @@ func GetVMWatchBinaryFullPath(processDirectory string) string {
return filepath.Join(processDirectory, "VMWatch", binaryName)
}

func GetVMWatchEnvironmentVariables(parameterOverrides map[string]interface{}, h vmextension.HandlerEnvironment) []string {
func GetVMWatchEnvironmentVariables(parameterOverrides map[string]interface{}, hEnv HandlerEnvironment) []string {
var arr []string
for key, value := range parameterOverrides {
arr = append(arr, fmt.Sprintf("%s=%s", key, value))
}

arr = append(arr, fmt.Sprintf("SIGNAL_FOLDER=%s", HandlerEnvironmentEventsFolderPath))
arr = append(arr, fmt.Sprintf("VERBOSE_LOG_FILE_FULL_PATH=%s", filepath.Join(h.HandlerEnvironment.LogFolder, VMWatchVerboseLogFileName)))
arr = append(arr, fmt.Sprintf("SIGNAL_FOLDER=%s", hEnv.HandlerEnvironment.EventsFolder))
arr = append(arr, fmt.Sprintf("VERBOSE_LOG_FILE_FULL_PATH=%s", filepath.Join(hEnv.HandlerEnvironment.LogFolder, VMWatchVerboseLogFileName)))

return arr
}
2 changes: 1 addition & 1 deletion misc/manifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<ExtensionImage xmlns="http://schemas.microsoft.com/windowsazure">
<ProviderNameSpace>Microsoft.ManagedServices</ProviderNameSpace>
<Type>ApplicationHealthLinux</Type>
<Version>2.0.7</Version>
<Version>2.0.8</Version>
<Label>Microsoft Azure Application Health Extension for Linux Virtual Machines</Label>
<HostingResources>VmRole</HostingResources>
<MediaLink></MediaLink>
Expand Down