2018-06-26 22:45:05 +03:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type StatusReport []StatusItem
|
|
|
|
|
|
|
|
type StatusItem struct {
|
|
|
|
Version float64 `json:"version"`
|
|
|
|
TimestampUTC string `json:"timestampUTC"`
|
|
|
|
Status Status `json:"status"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type StatusType string
|
|
|
|
|
|
|
|
const (
|
|
|
|
StatusTransitioning StatusType = "transitioning"
|
Bump to v2.0.8: Bootstrap VMWatch Process if vmWatchSettings present (#33)
## Overview
This PR contains changes to support running VMWatch (amd64 and arm64) as an executable via goroutines and channels.
> VMWatch is a standardized, lightweight, and open-sourced testing framework designed to enhance the monitoring and management of guest VMs on the Azure platform, including both 1P and 3P instances. VMWatch is engineered to collect vital health signals across multiple dimensions, which will be seamlessly integrated into Azure's quality systems. By leveraging these signals, VMWatch will enable Azure to swiftly detect and prevent regressions induced by platform updates or configuration changes, identify gaps in platform telemetry, and ultimately improve the guest experience for all Azure customers.
## Behavior
VMWatch will run asynchronously as a separate process than ApplicationHealth, so the probing of application health will not be affected by the state of VMWatch. Depending on extension settings, VMWatch can be enabled/disabled, and also specify the test names and parameter overrides to VMWatch binary. The status of VMWatch will be displayed in the extension x.status files and also in GET VM Instance View. Main process will attempt to start VMWatch binary up to 3 times, after which VMWatch status will be set to failed.
## Process Leaks
To ensure that VMWatch processes do not accumulate, applicationhealth-shim will be responsible for killing existing VMWatch processes by looking for processes running with the VMWatch binary names according to the architecture type. For unexpected process termination, if for some reason the main applicationhealth-extension process is terminated, we also ensure that the VMWatch process is also killed by subscribing to shutdown/termination signals in the main process, and killing the VMWatch based off process ID.
## Example Binary Execution
Example execution from integration testing
`
SIGNAL_FOLDER=/var/log/azure/Microsoft.ManagedServices.ApplicationHealthLinux/events VERBOSE_LOG_FILE_FULL_PATH=/var/log/azure/Microsoft.ManagedServices.ApplicationHealthLinux/VE.RS.ION/vmwatch.log ./var/lib/waagent/Extension/bin/VMWatch/vmwatch_linux_amd64 --config /var/lib/waagent/Extension/bin/VMWatch/vmwatch.conf --input-filter disk_io:outbound_connectivity
`
## Release/Packaging
In addition to the arm64 or amd64 VMWatch binaries, `vmwatch.conf` will be expected to be present in the bin/VMWatch directory for VMWatch process to read. VMWatch will also be populating and sharing eventsFolder with ApplicationHealth, so events can be viewed in Kusto. The verbose logs of VMWatch will be written to `vmwatch.log`.
---------
Co-authored-by: klugorosado <142627157+klugorosado@users.noreply.github.com>
2023-11-17 19:25:58 +03:00
|
|
|
StatusWarning StatusType = "warning"
|
2018-06-26 22:45:05 +03:00
|
|
|
StatusError StatusType = "error"
|
|
|
|
StatusSuccess StatusType = "success"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Status struct {
|
|
|
|
Operation string `json:"operation"`
|
|
|
|
ConfigurationAppliedTimeUTC string `json:"configurationAppliedTime"`
|
|
|
|
Status StatusType `json:"status"`
|
|
|
|
FormattedMessage FormattedMessage `json:"formattedMessage"`
|
|
|
|
SubstatusList []SubstatusItem `json:"substatus,omitempty"`
|
|
|
|
}
|
2022-07-14 18:33:38 +03:00
|
|
|
|
2018-06-26 22:45:05 +03:00
|
|
|
type FormattedMessage struct {
|
|
|
|
Lang string `json:"lang"`
|
|
|
|
Message string `json:"message"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type SubstatusItem struct {
|
2022-08-12 21:01:24 +03:00
|
|
|
Name string `json:"name"`
|
|
|
|
Status StatusType `json:"status"`
|
|
|
|
FormattedMessage FormattedMessage `json:"formattedMessage"`
|
2018-06-26 22:45:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewStatus(t StatusType, operation, message string) StatusReport {
|
|
|
|
now := time.Now().UTC().Format(time.RFC3339)
|
|
|
|
return []StatusItem{
|
|
|
|
{
|
2022-08-12 21:01:24 +03:00
|
|
|
Version: 1.0,
|
2018-06-26 22:45:05 +03:00
|
|
|
TimestampUTC: now,
|
2022-08-12 21:01:24 +03:00
|
|
|
Status: Status{
|
|
|
|
Operation: operation,
|
2018-06-26 22:45:05 +03:00
|
|
|
ConfigurationAppliedTimeUTC: now,
|
2023-08-24 01:21:23 +03:00
|
|
|
Status: t,
|
2018-06-26 22:45:05 +03:00
|
|
|
FormattedMessage: FormattedMessage{
|
2022-08-12 21:01:24 +03:00
|
|
|
Lang: "en",
|
2022-07-14 18:33:38 +03:00
|
|
|
Message: message,
|
|
|
|
},
|
2018-06-26 22:45:05 +03:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-22 22:04:21 +03:00
|
|
|
func NewSubstatus(name string, t StatusType, message string) SubstatusItem {
|
2022-08-27 01:17:51 +03:00
|
|
|
return SubstatusItem{
|
2022-08-22 22:04:21 +03:00
|
|
|
Name: name,
|
|
|
|
Status: t,
|
|
|
|
FormattedMessage: FormattedMessage{
|
|
|
|
Lang: "en",
|
|
|
|
Message: message,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-14 18:33:38 +03:00
|
|
|
func (r StatusReport) AddSubstatus(t StatusType, name, message string, state HealthStatus) {
|
2018-06-26 22:45:05 +03:00
|
|
|
if len(r) > 0 {
|
2022-08-12 21:01:24 +03:00
|
|
|
substatusItem := SubstatusItem{
|
|
|
|
Name: name,
|
|
|
|
Status: t,
|
|
|
|
FormattedMessage: FormattedMessage{
|
|
|
|
Lang: "en",
|
|
|
|
Message: message,
|
2018-06-26 22:45:05 +03:00
|
|
|
},
|
|
|
|
}
|
2022-08-12 21:01:24 +03:00
|
|
|
r[0].Status.SubstatusList = append(r[0].Status.SubstatusList, substatusItem)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r StatusReport) AddSubstatusItem(substatus SubstatusItem) {
|
|
|
|
if len(r) > 0 {
|
|
|
|
r[0].Status.SubstatusList = append(r[0].Status.SubstatusList, substatus)
|
2018-06-26 22:45:05 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r StatusReport) marshal() ([]byte, error) {
|
|
|
|
return json.MarshalIndent(r, "", "\t")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save persists the status message to the specified status folder using the
|
|
|
|
// sequence number. The operation consists of writing to a temporary file in the
|
|
|
|
// same folder and moving it to the final destination for atomicity.
|
2024-06-28 04:13:21 +03:00
|
|
|
func (r StatusReport) Save(statusFolder string, seqNum uint) error {
|
2018-06-26 22:45:05 +03:00
|
|
|
fn := fmt.Sprintf("%d.status", seqNum)
|
|
|
|
path := filepath.Join(statusFolder, fn)
|
|
|
|
tmpFile, err := ioutil.TempFile(statusFolder, fn)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("status: failed to create temporary file: %v", err)
|
|
|
|
}
|
|
|
|
tmpFile.Close()
|
|
|
|
|
|
|
|
b, err := r.marshal()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("status: failed to marshal into json: %v", err)
|
|
|
|
}
|
|
|
|
if err := ioutil.WriteFile(tmpFile.Name(), b, 0644); err != nil {
|
2022-07-14 18:33:38 +03:00
|
|
|
return fmt.Errorf("status: failed to write to path=%s error=%v", tmpFile.Name(), err)
|
2018-06-26 22:45:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.Rename(tmpFile.Name(), path); err != nil {
|
|
|
|
return fmt.Errorf("status: failed to move to path=%s error=%v", path, err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2024-05-22 01:02:52 +03:00
|
|
|
|
|
|
|
func (r StatusReport) String() string {
|
|
|
|
report, _ := json.MarshalIndent(r, "", "\t")
|
|
|
|
return string(report)
|
|
|
|
}
|