internal/hostgacommunicator/vmsettings.go (83 lines of code) (raw):
package hostgacommunicator
import (
"fmt"
"net/http"
"os"
"path/filepath"
"time"
"github.com/Azure/run-command-handler-linux/internal/constants"
"github.com/Azure/run-command-handler-linux/internal/handlersettings"
requesthelper "github.com/Azure/run-command-handler-linux/internal/requesthelper"
"github.com/Azure/run-command-handler-linux/internal/settings"
"github.com/go-kit/kit/log"
"github.com/pkg/errors"
)
const (
vmSettingsOperation = "immediateGoalState"
)
const (
vmSettingsRequestTimeout = 30 * time.Second
)
type VMImmediateExtensionsGoalState struct {
ImmediateExtensionGoalStates []ImmediateExtensionGoalState `json:"immediateExtensionsGoalStates"`
}
type ImmediateExtensionGoalState struct {
Name string `json:"name"`
Settings []settings.SettingsCommon `json:"settings"`
}
// Struct used to wrap the url to use when making requests
type requestFactory struct {
url string
}
// Returns a new RequestManager object useful to make GET Requests
func GetVMSettingsRequestManager(ctx *log.Context) (*requesthelper.RequestManager, error) {
factory, err := newVMSettingsRequestFactory(ctx)
if err != nil {
return nil, errors.Wrapf(err, "failed to create request factory")
}
return requesthelper.GetRequestManager(factory, vmSettingsRequestTimeout), nil
}
// Returns a new requestFactory object with the VMSettings API Uri set
func newVMSettingsRequestFactory(ctx *log.Context) (*requestFactory, error) {
ctx.Log("message", "trying to create new request factory")
url, err := getOperationUri(ctx, vmSettingsOperation)
if err != nil {
return nil, errors.Wrapf(err, "failed to obtain VMSettingsURI")
}
return &requestFactory{url}, nil
}
// GetRequest returns a new request with the provided url
func (u requestFactory) GetRequest(ctx *log.Context, eTag string) (*http.Request, error) {
ctx.Log("message", fmt.Sprintf("performing make request to %v", u.url))
request, err := http.NewRequest("GET", u.url, nil)
if err != nil {
return nil, errors.Wrap(err, "failed to create request")
}
if eTag != "" {
ctx.Log("message", "setting request headers to include ETag "+eTag)
request.Header.Set(constants.IfNoneMatchHeaderName, eTag)
}
return request, err
}
func (goalState *ImmediateExtensionGoalState) ValidateSignature() (bool, error) {
he, err := handlersettings.GetHandlerEnv()
if err != nil {
return false, errors.Wrap(err, "failed to parse handlerenv")
}
configFolder := he.HandlerEnvironment.ConfigFolder
// TODO: Check that certificate exists or download it if is missing
// Do we need to re-download or can we assume the cert is already there?
for _, s := range goalState.Settings {
if s.ProtectedSettingsBase64 == "" {
continue
}
if s.SettingsCertThumbprint == "" {
return false, errors.New("HandlerSettings has protected settings but no cert thumbprint")
}
// go two levels up where certs are placed (/var/lib/waagent)
crt := filepath.Join(configFolder, "..", "..", fmt.Sprintf("%s.crt", s.SettingsCertThumbprint))
prv := filepath.Join(configFolder, "..", "..", fmt.Sprintf("%s.prv", s.SettingsCertThumbprint))
if !fileExists(crt) || !fileExists(prv) {
message := fmt.Sprintf("Certificate %v needed by %v is missing from the goal state", s.SettingsCertThumbprint, s.ExtensionName)
return false, errors.New(message)
}
}
return true, nil
}
// Checks if the given file exists
func fileExists(filename string) bool {
_, err := os.Stat(filename)
return !os.IsNotExist(err)
}