-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhostgacommunicator.go
More file actions
233 lines (201 loc) · 7.43 KB
/
hostgacommunicator.go
File metadata and controls
233 lines (201 loc) · 7.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
package hostgacommunicator
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"github.com/Azure/VMApplication-Extension/internal/requesthelper"
"github.com/Azure/azure-extension-platform/pkg/logging"
"github.com/pkg/errors"
)
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
DownloadConfig(el *logging.ExtensionLogger, appName string, dst string) error
GetVMAppInfo(el *logging.ExtensionLogger, appName string) (*VMAppMetadata, error)
}
// HostGaCommunicator provides methods for retrieving application metadata and packages
// from the HostGaPlugin
type HostGaCommunicator struct{}
// GetVMAppInfo returns the metadata for the application
func (*HostGaCommunicator) GetVMAppInfo(el *logging.ExtensionLogger, appName string) (*VMAppMetadata, error) {
requestManager, isArc, err := getMetadataRequestManager(el, appName)
if err != nil {
return nil, &HostGaCommunicatorGetVMAppInfoError{
errorMessage: fmt.Sprintf("Could not create the request manager: %v", err),
errorType: InitializationError,
}
}
var resp *http.Response
if isArc {
// Use Arc authentication for Arc endpoints
arcHandler := requesthelper.NewArcAuthHandler(requestManager)
resp, err = requesthelper.WithRetriesArc(el, arcHandler, requesthelper.ActualSleep)
} else {
// Use standard retry logic for non-Arc endpoints
resp, err = requesthelper.WithRetries(el, requestManager, requesthelper.ActualSleep)
}
if err != nil {
return nil, &HostGaCommunicatorGetVMAppInfoError{
errorMessage: fmt.Sprintf("Metadata request failed after retries: %v", err),
errorType: MetadataRequestFailedWithRetries,
}
}
body := resp.Body
defer body.Close()
var target VMAppMetadataReceiver
err = json.NewDecoder(body).Decode(&target)
if err != nil {
return nil, &HostGaCommunicatorGetVMAppInfoError{
errorMessage: fmt.Sprintf("Failed to decode response body: %v", err),
errorType: MetadataRequestFailedInvalidResponseBody,
}
}
return target.MapToVMAppMetadata(), nil
}
// DownloadPackage downloads the application package through HostGaPlugin to the specified
// file. If the download fails, it automatically retrieves at the last received bytes
// and rebuilds the file from downloaded parts
func (*HostGaCommunicator) DownloadPackage(el *logging.ExtensionLogger, appName string, dst string) error {
requestFactory, err := newPackageDownloadRequestFactory(el, appName)
if err != nil {
return &DownloadPackageError{
errorMessage: fmt.Sprintf("Could not create the request factory: %v", err),
errorType: DownloadPackageRequestFactoryError,
}
}
err = requestFactory.downloadFile(el, dst)
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
// file. If the download fails, it automatically retrieves at the last received bytes
// and rebuilds the file from downloaded parts
func (*HostGaCommunicator) DownloadConfig(el *logging.ExtensionLogger, appName string, dst string) error {
requestFactory, err := newConfigDownloadRequestFactory(el, appName)
if err != nil {
return &DownloadConfigError{
errorMessage: fmt.Sprintf("Could not create the request factory: %v", err),
errorType: DownloadConfigRequestFactoryError,
}
}
err = requestFactory.downloadFile(el, dst)
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) {
baseAddress := os.Getenv(WireProtocolAddress)
if baseAddress != "" {
return buildUriUsingWireProtocolAddress(baseAddress, appName, operation)
}
var baseEndpoint string
isArcPresent := isArcAgentPresent(el)
if isArcPresent {
arcEndpoint := getArcEndpoint(el)
el.Info("Arc agent detected, using Arc endpoint: %s", arcEndpoint)
baseEndpoint = arcEndpoint
} else {
el.Warn("environment variable %s not set, using WireProtocol fallback address", WireProtocolAddress)
baseEndpoint = wireServerFallbackAddress
}
uri, _ := url.Parse(baseEndpoint)
// For both Arc and Azure, use the same path structure
uri.Path = fmt.Sprintf("applications/%s/%s", appName, operation)
return uri.String(), nil
}
func buildUriUsingWireProtocolAddress(baseAddress string, appName string, operation string) (string, error) {
uri, err := url.Parse(baseAddress)
if err != nil {
// ip with port 10.0.0.1:1234 will fail otherwise
uri, err = url.Parse("//" + baseAddress)
if err != nil {
return "", errors.Wrap(err, "Could not parse the HostGA URI")
}
}
if uri.Host == "" {
// takes care of host names without port like foo.bar.com, 10.0.0.1, these need to be prepended with //
uri, err = url.Parse("//" + baseAddress)
if err != nil {
return "", errors.Wrap(err, "Could not parse the HostGA URI")
}
}
// if port is not specified, set default port
if uri.Port() == "" {
uri, err = url.Parse("//" + uri.Host + ":" + hostGaPluginPort)
if err != nil {
return "", errors.Wrap(err, "failed to add default host ga plugin port")
}
}
uri.Path = fmt.Sprintf("applications/%s/%s", appName, operation)
if uri.Scheme == "" {
uri.Scheme = "http"
}
return uri.String(), nil
}