Skip to content
Open
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
102 changes: 95 additions & 7 deletions internal/hostgacommunicator/hostgacommunicator.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,67 @@ import (
const hostGaPluginPort = "32526"
const WireProtocolAddress = "AZURE_GUEST_AGENT_WIRE_PROTOCOL_ADDRESS"
const wireServerFallbackAddress = "http://168.63.129.16:32526"
const HostGaMetadataErrorPrefix = "HostGaCommunicator GetVMAppInfo error"

type HostGaCommunicatorError int

const (
InitializationError HostGaCommunicatorError = iota
MetadataRequestFailedWithRetries
MetadataRequestFailedInvalidResponseBody
DownloadPackageRequestFactoryError
DownloadPackageFileError
DownloadConfigRequestFactoryError
DownloadConfigFileError
)

func (hostGaCommunicatorError HostGaCommunicatorError) ToString() string {
switch hostGaCommunicatorError {
case InitializationError:
return "InitializationError"
case MetadataRequestFailedWithRetries:
return "MetadataRequestFailedWithRetries"
case MetadataRequestFailedInvalidResponseBody:
return "MetadataRequestFailedInvalidResponseBody"
case DownloadPackageRequestFactoryError:
return "DownloadPackageRequestFactoryError"
case DownloadPackageFileError:
return "DownloadPackageFileError"
case DownloadConfigRequestFactoryError:
return "DownloadConfigRequestFactoryError"
case DownloadConfigFileError:
return "DownloadConfigFileError"
default:
return "UnknownError"
}
}

type HostGaCommunicatorGetVMAppInfoError struct {
errorMessage string
errorType HostGaCommunicatorError
}

func (e *HostGaCommunicatorGetVMAppInfoError) Error() string {
return fmt.Sprintf("%s: %s, error type: %s", HostGaMetadataErrorPrefix, e.errorMessage, e.errorType.ToString())
}

type DownloadPackageError struct {
errorMessage string
errorType HostGaCommunicatorError
}

func (e *DownloadPackageError) Error() string {
return fmt.Sprintf("DownloadPackage error: %s, error type: %s", e.errorMessage, e.errorType.ToString())
}

type DownloadConfigError struct {
errorMessage string
errorType HostGaCommunicatorError
}

func (e *DownloadConfigError) Error() string {
return fmt.Sprintf("DownloadConfig error: %s, error type: %s", e.errorMessage, e.errorType.ToString())
}

type IHostGaCommunicator interface {
DownloadPackage(el *logging.ExtensionLogger, appName string, dst string) error
Expand All @@ -33,7 +94,10 @@ type HostGaCommunicator struct{}
func (*HostGaCommunicator) GetVMAppInfo(el *logging.ExtensionLogger, appName string) (*VMAppMetadata, error) {
requestManager, isArc, err := getMetadataRequestManager(el, appName)
if err != nil {
return nil, errors.Wrapf(err, "Could not create the request manager")
return nil, &HostGaCommunicatorGetVMAppInfoError{
errorMessage: fmt.Sprintf("Could not create the request manager: %v", err),
errorType: InitializationError,
}
}

var resp *http.Response
Expand All @@ -47,7 +111,10 @@ func (*HostGaCommunicator) GetVMAppInfo(el *logging.ExtensionLogger, appName str
}

if err != nil {
return nil, errors.Wrapf(err, "Metadata request failed with retries.")
return nil, &HostGaCommunicatorGetVMAppInfoError{
errorMessage: fmt.Sprintf("Metadata request failed after retries: %v", err),
errorType: MetadataRequestFailedWithRetries,
}
}

body := resp.Body
Expand All @@ -56,7 +123,10 @@ func (*HostGaCommunicator) GetVMAppInfo(el *logging.ExtensionLogger, appName str
var target VMAppMetadataReceiver
err = json.NewDecoder(body).Decode(&target)
if err != nil {
return nil, errors.Wrapf(err, "failed to decode response body")
return nil, &HostGaCommunicatorGetVMAppInfoError{
errorMessage: fmt.Sprintf("Failed to decode response body: %v", err),
errorType: MetadataRequestFailedInvalidResponseBody,
}
}

return target.MapToVMAppMetadata(), nil
Expand All @@ -68,11 +138,20 @@ func (*HostGaCommunicator) GetVMAppInfo(el *logging.ExtensionLogger, appName str
func (*HostGaCommunicator) DownloadPackage(el *logging.ExtensionLogger, appName string, dst string) error {
requestFactory, err := newPackageDownloadRequestFactory(el, appName)
if err != nil {
return errors.Wrapf(err, "Could not create the request factory")
return &DownloadPackageError{
errorMessage: fmt.Sprintf("Could not create the request factory: %v", err),
errorType: DownloadPackageRequestFactoryError,
}
}

err = requestFactory.downloadFile(el, dst)
return err
if err != nil {
return &DownloadPackageError{
errorMessage: fmt.Sprintf("Failed to download file: %v", err),
errorType: DownloadPackageFileError,
}
}
return nil
}

// DownloadConfig downloads the application config through HostGaPlugin to the specified
Expand All @@ -81,11 +160,20 @@ func (*HostGaCommunicator) DownloadPackage(el *logging.ExtensionLogger, appName
func (*HostGaCommunicator) DownloadConfig(el *logging.ExtensionLogger, appName string, dst string) error {
requestFactory, err := newConfigDownloadRequestFactory(el, appName)
if err != nil {
return errors.Wrapf(err, "Could not create the request factory")
return &DownloadConfigError{
errorMessage: fmt.Sprintf("Could not create the request factory: %v", err),
errorType: DownloadConfigRequestFactoryError,
}
}

err = requestFactory.downloadFile(el, dst)
return err
if err != nil {
return &DownloadConfigError{
errorMessage: fmt.Sprintf("Failed to download file: %v", err),
errorType: DownloadConfigFileError,
}
}
return nil
}

func getOperationURI(el *logging.ExtensionLogger, appName string, operation string) (string, error) {
Expand Down
63 changes: 61 additions & 2 deletions internal/hostgacommunicator/hostgacommunicator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ func TestGetVmAppInfo_InvalidUri(t *testing.T) {
hgc := &HostGaCommunicator{}
_, err := hgc.GetVMAppInfo(nopLog(), myAppName)
require.NotNil(t, err, "did not fail")
_, ok := err.(*HostGaCommunicatorGetVMAppInfoError)
require.True(t, ok, "expected error to be of type *HostGaCommunicatorGetVMAppInfoError")
require.Contains(t, err.Error(), InitializationError.ToString(), "Wrong error code")
require.Contains(t, err.Error(), "Could not parse the HostGA URI", "Wrong message for invalid uri")
}

Expand All @@ -60,7 +63,10 @@ func TestGetVmAppInfo_RequestFailed(t *testing.T) {
hgc := &HostGaCommunicator{}
_, err := hgc.GetVMAppInfo(nopLog(), myAppName)
require.NotNil(t, err, "did not fail")
require.Contains(t, err.Error(), "Metadata request failed with retries.", "Wrong message for failed request")
_, ok := err.(*HostGaCommunicatorGetVMAppInfoError)
require.True(t, ok, "expected error to be of type *HostGaCommunicatorGetVMAppInfoError")
require.Contains(t, err.Error(), MetadataRequestFailedWithRetries.ToString(), "Wrong error code")
require.Contains(t, err.Error(), "Metadata request failed after retries:", "Wrong message for failed request")
}

func TestGetVmAppInfo_CouldNotDecodeResponse(t *testing.T) {
Expand All @@ -75,7 +81,10 @@ func TestGetVmAppInfo_CouldNotDecodeResponse(t *testing.T) {
hgc := &HostGaCommunicator{}
_, err := hgc.GetVMAppInfo(nopLog(), myAppName)
require.NotNil(t, err, "did not fail")
require.Contains(t, err.Error(), "failed to decode response body", "Wrong message for invalid response")
_, ok := err.(*HostGaCommunicatorGetVMAppInfoError)
require.True(t, ok, "expected error to be of type *HostGaCommunicatorGetVMAppInfoError")
require.Contains(t, err.Error(), MetadataRequestFailedInvalidResponseBody.ToString(), "Wrong error code")
require.Contains(t, err.Error(), "Failed to decode response body:", "Wrong message for invalid response")
}

func TestGetVmAppInfo_MissingProperties(t *testing.T) {
Expand Down Expand Up @@ -155,9 +164,22 @@ func TestDownloadPackage_CannotRemoveExistingFile(t *testing.T) {
hgc := &HostGaCommunicator{}
err = hgc.DownloadPackage(nopLog(), myAppName, filePath)
require.NotNil(t, err, "did not fail")
_, ok := err.(*DownloadPackageError)
require.True(t, ok, "expected error to be of type *DownloadPackageError")
require.Contains(t, err.Error(), "DownloadPackageFileError", "Wrong error code")
require.Contains(t, err.Error(), "Could not remove the existing file", "Wrong message for failing to remove locked file")
}

func TestDownloadPackage_InvalidUri(t *testing.T) {
os.Setenv(WireProtocolAddress, "htt!p:notgoingtohappen!")
hgc := &HostGaCommunicator{}
err := hgc.DownloadPackage(nopLog(), myAppName, "somepath")
require.NotNil(t, err, "did not fail")
_, ok := err.(*DownloadPackageError)
require.True(t, ok, "expected error to be of type *DownloadPackageError")
require.Contains(t, err.Error(), DownloadPackageRequestFactoryError.ToString(), "Wrong error type")
}

func TestDownloadPackage_InvalidPath(t *testing.T) {
filePath := string(make([]byte, 5)) // null characters in file names are invalid in both windows and linux

Expand All @@ -170,6 +192,9 @@ func TestDownloadPackage_InvalidPath(t *testing.T) {
hgc := &HostGaCommunicator{}
err := hgc.DownloadPackage(nopLog(), myAppName, filePath)
require.NotNil(t, err, "did not fail")
_, ok := err.(*DownloadPackageError)
require.True(t, ok, "expected error to be of type *DownloadPackageError")
require.Contains(t, err.Error(), "DownloadPackageFileError", "Wrong error code")
require.Contains(t, err.Error(), "Cannot retrieve file information", "Wrong message for invalid file path")
}

Expand Down Expand Up @@ -220,6 +245,9 @@ func TestDownloadPackage_TooManyTries(t *testing.T) {
hgc := &HostGaCommunicator{}
err := hgc.DownloadPackage(nopLog(), myAppName, filePath)
require.NotNil(t, err, "did not fail")
_, ok := err.(*DownloadPackageError)
require.True(t, ok, "expected error to be of type *DownloadPackageError")
require.Contains(t, err.Error(), "DownloadPackageFileError", "Wrong error code")
require.Contains(t, err.Error(), "Failed to completely download the file", "Wrong message for incomplete file")
}

Expand Down Expand Up @@ -249,6 +277,9 @@ func TestDownloadPackage_IntermediateCallFails(t *testing.T) {
hgc := &HostGaCommunicator{}
err := hgc.DownloadPackage(nopLog(), myAppName, filePath)
require.NotNil(t, err, "did not fail")
_, ok := err.(*DownloadPackageError)
require.True(t, ok, "expected error to be of type *DownloadPackageError")
require.Contains(t, err.Error(), "DownloadPackageFileError", "Wrong error code")
require.Contains(t, err.Error(), "Unrecoverable error while downloading the file", "Wrong message for failure mid-retries")
}

Expand Down Expand Up @@ -288,6 +319,34 @@ func TestDownloadPackage_MultipleCallDownload(t *testing.T) {
verifyFileContents(t, filePath, expected)
}

func TestDownloadConfig_InvalidUri(t *testing.T) {
os.Setenv(WireProtocolAddress, "htt!p:notgoingtohappen!")
hgc := &HostGaCommunicator{}
err := hgc.DownloadConfig(nopLog(), myAppName, "somepath")
require.NotNil(t, err, "did not fail")
_, ok := err.(*DownloadConfigError)
require.True(t, ok, "expected error to be of type *DownloadConfigError")
require.Contains(t, err.Error(), DownloadConfigRequestFactoryError.ToString(), "Wrong error code")
}

func TestDownloadConfig_InvalidPath(t *testing.T) {
filePath := string(make([]byte, 5)) // null characters in file names are invalid in both windows and linux

srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer srv.Close()

os.Setenv(WireProtocolAddress, srv.URL)
hgc := &HostGaCommunicator{}
err := hgc.DownloadConfig(nopLog(), myAppName, filePath)
require.NotNil(t, err, "did not fail")
_, ok := err.(*DownloadConfigError)
require.True(t, ok, "expected error to be of type *DownloadConfigError")
require.Contains(t, err.Error(), DownloadConfigFileError.ToString(), "Wrong error code")
require.Contains(t, err.Error(), "Cannot retrieve file information", "Wrong message for invalid file path")
}

func TestDownloadConfig_SingeCallDownload(t *testing.T) {
expected := "file contents don't matter"
createTestDir(t)
Expand Down
2 changes: 1 addition & 1 deletion launcher/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func main() {

if requestedSequenceNumber >= currentSequenceNumber {
// attempt to write a transitioning status file if it doesn't exist
_, getStatusError := utils.GetStatusType(handlerEnv, requestedSequenceNumber)
_, getStatusError := utils.GetStatus(handlerEnv, requestedSequenceNumber)
if getStatusError != nil {
// either no transitioning status file was found, or the status file was malformed
// either way create a new transitioning status file
Expand Down
Loading
Loading