311 строки
9.0 KiB
Go
311 строки
9.0 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
|
|
"github.com/Azure/azure-extension-foundation/sequence"
|
|
"github.com/Azure/azure-extension-foundation/settings"
|
|
"github.com/Azure/azure-extension-foundation/status"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var (
|
|
versionMajor = "1"
|
|
versionMinor = "0"
|
|
versionBuild = "0"
|
|
versionRevision = "0"
|
|
version = fmt.Sprintf("%s.%s.%s.%s", versionMajor, versionMinor, versionBuild, versionRevision)
|
|
|
|
extensionMrSeq int
|
|
environmentMrSeq int
|
|
|
|
// Logging is currently set up to create/add to the logile in the directory from where the binary is executed
|
|
// TODO Read this in from Handler Env
|
|
logfile string
|
|
operationLogfile string
|
|
|
|
extensionName = "GuestAgentTestExtension"
|
|
|
|
infoLogger, warningLogger, errorLogger customLogger
|
|
|
|
failCommands []string
|
|
)
|
|
|
|
const (
|
|
// Exit Codes
|
|
successfulExecution = iota // 0
|
|
generalExitError // 1
|
|
commandNotFoundError // 2
|
|
logfileNotOpenedError // 3
|
|
mrSeqNotFoundError // 4
|
|
shouldNotRunError // 5
|
|
seqNumberSetError // 6
|
|
statusReportingError // 7
|
|
settingsNotFoundError // 8
|
|
versionMismatchError // 9
|
|
jsonParsingError // 10
|
|
)
|
|
|
|
// extension specific PublicSettings
|
|
type PublicSettings struct {
|
|
Script string `json:"script"`
|
|
FileURLs []string `json:"fileUris"`
|
|
}
|
|
|
|
// extension specific ProtectedSettings
|
|
type ProtectedSettings struct {
|
|
SecretString string `json:"secretString"`
|
|
SecretScript string `json:"secretScript"`
|
|
FileURLs []string `json:"fileUris"`
|
|
StorageAccountName string `json:"storageAccountName"`
|
|
StorageAccountKey string `json:"storageAccountKey"`
|
|
}
|
|
|
|
func install() {
|
|
operation := "install"
|
|
infoLogger.Printf("Extension MrSeq: %d, Environment MrSeq: %d", extensionMrSeq, environmentMrSeq)
|
|
|
|
err := status.ReportTransitioning(environmentMrSeq, operation, "installation in progress")
|
|
infoLogger.Println("Installation in progress")
|
|
if err != nil {
|
|
errorLogger.Printf("Status reporting error: %+v", err)
|
|
os.Exit(statusReportingError)
|
|
}
|
|
|
|
for _, value := range failCommands {
|
|
if value == operation {
|
|
errorLogger.Printf("%s failed based on provided failCommand", operation)
|
|
panic(fmt.Sprintf("%s failed based on provided failCommand", operation))
|
|
}
|
|
}
|
|
|
|
err = status.ReportSuccess(environmentMrSeq, operation, "installation is complete")
|
|
if err != nil {
|
|
errorLogger.Printf("Status reporting error: %+v", err)
|
|
os.Exit(statusReportingError)
|
|
}
|
|
infoLogger.Println("Installation is complete")
|
|
os.Exit(successfulExecution)
|
|
}
|
|
|
|
func enable() {
|
|
operation := "enable"
|
|
|
|
err := status.ReportTransitioning(environmentMrSeq, operation, "enabling in progress")
|
|
infoLogger.Println("enabling in progress")
|
|
if err != nil {
|
|
errorLogger.Printf("%+v", err)
|
|
os.Exit(statusReportingError)
|
|
}
|
|
|
|
for _, value := range failCommands {
|
|
if value == operation {
|
|
errorLogger.Printf("%s failed based on provided failCommand", operation)
|
|
panic(fmt.Sprintf("%s failed based on provided failCommand", operation))
|
|
}
|
|
}
|
|
|
|
var publicSettings PublicSettings
|
|
var protectedSettings ProtectedSettings
|
|
|
|
err = settings.GetExtensionSettings(environmentMrSeq, &publicSettings, &protectedSettings)
|
|
if err != nil {
|
|
status.ReportError(environmentMrSeq, operation, err.Error())
|
|
errorLogger.Printf("%+v", err)
|
|
os.Exit(settingsNotFoundError)
|
|
}
|
|
infoLogger.Printf("Public Settings: %v \t Protected Settings: %v", publicSettings, protectedSettings)
|
|
|
|
err = status.ReportSuccess(environmentMrSeq, operation, "enabling is complete")
|
|
if err != nil {
|
|
errorLogger.Printf("%+v", err)
|
|
os.Exit(statusReportingError)
|
|
}
|
|
infoLogger.Println("enabling is complete")
|
|
os.Exit(successfulExecution)
|
|
}
|
|
|
|
func disable() {
|
|
operation := "disable"
|
|
|
|
err := status.ReportTransitioning(environmentMrSeq, operation, "disabling in progress")
|
|
infoLogger.Println("disabling in progress")
|
|
if err != nil {
|
|
errorLogger.Printf("%+v", err)
|
|
os.Exit(statusReportingError)
|
|
}
|
|
|
|
for _, value := range failCommands {
|
|
if value == operation {
|
|
errorLogger.Printf("%s failed based on provided failCommand", operation)
|
|
panic(fmt.Sprintf("%s failed based on provided failCommand", operation))
|
|
}
|
|
}
|
|
|
|
err = status.ReportSuccess(environmentMrSeq, operation, "disabling is complete")
|
|
if err != nil {
|
|
errorLogger.Printf("%+v", err)
|
|
os.Exit(statusReportingError)
|
|
}
|
|
infoLogger.Println("disabling is complete")
|
|
os.Exit(successfulExecution)
|
|
}
|
|
|
|
func uninstall() {
|
|
operation := "uninstall"
|
|
|
|
err := status.ReportTransitioning(environmentMrSeq, operation, "uninstallation in progress")
|
|
infoLogger.Println("uninstallation in progress")
|
|
if err != nil {
|
|
errorLogger.Printf("%+v", err)
|
|
os.Exit(statusReportingError)
|
|
}
|
|
|
|
for _, value := range failCommands {
|
|
if value == operation {
|
|
errorLogger.Printf("%s failed based on provided failCommand", operation)
|
|
panic(fmt.Sprintf("%s failed based on provided failCommand", operation))
|
|
}
|
|
}
|
|
|
|
err = status.ReportSuccess(environmentMrSeq, operation, "uninstallation is complete")
|
|
|
|
if err != nil {
|
|
errorLogger.Printf("%+v", err)
|
|
os.Exit(statusReportingError)
|
|
}
|
|
infoLogger.Println("uninstallation is complete")
|
|
os.Exit(successfulExecution)
|
|
}
|
|
|
|
func update() {
|
|
operation := "update"
|
|
|
|
err := status.ReportTransitioning(environmentMrSeq, operation, "updating in progress")
|
|
infoLogger.Println("updating in progress")
|
|
if err != nil {
|
|
errorLogger.Printf("%+v", err)
|
|
os.Exit(statusReportingError)
|
|
}
|
|
|
|
for _, value := range failCommands {
|
|
if value == operation {
|
|
errorLogger.Printf("%s failed based on provided failCommand", operation)
|
|
panic(fmt.Sprintf("%s failed based on provided failCommand", operation))
|
|
}
|
|
}
|
|
|
|
err = status.ReportSuccess(environmentMrSeq, operation, "updating is complete")
|
|
|
|
if err != nil {
|
|
errorLogger.Printf("%+v", err)
|
|
os.Exit(statusReportingError)
|
|
}
|
|
infoLogger.Println("updating is complete")
|
|
os.Exit(successfulExecution)
|
|
}
|
|
|
|
func parseJSON(filename string) error {
|
|
// Open the provided file
|
|
jsonFile, err := os.Open(filename)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "Failed to open \"%s\"", filename)
|
|
}
|
|
infoLogger.Println("File opened successfully")
|
|
|
|
// Defer file closing until parseJSON() returns
|
|
defer jsonFile.Close()
|
|
|
|
// Unmarshall the bytes from the JSON file
|
|
byteValue, _ := ioutil.ReadAll(jsonFile)
|
|
var jsonData map[string][]string
|
|
json.Unmarshal([]byte(byteValue), &jsonData)
|
|
|
|
failCommands = jsonData["failCommands"]
|
|
return nil
|
|
}
|
|
|
|
/* Open the logfile and configure the loggers that will be used
|
|
*
|
|
* The main difference between types of loggers is the label (eg INFO) and additional data provided .
|
|
*/
|
|
func initLogging() (*os.File, error) {
|
|
handlerEnv, err := settings.GetHandlerEnvironment()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "Failed to open handler environment")
|
|
}
|
|
logfileLogName := extensionName + "-" + version + ".log"
|
|
logfile = path.Join(handlerEnv.HandlerEnvironment.LogFolder, logfileLogName)
|
|
|
|
file, err := os.OpenFile(logfile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "Failed to create/open %s", logfile)
|
|
}
|
|
|
|
//Sample: [2020-08-18T20:29:16.079902Z] [1.0.0.0] [main.go:148] [INFO]: Test1
|
|
infoLogger = customLogger{log.New(io.MultiWriter(file, os.Stdout), "", 0), infoOperation}
|
|
warningLogger = customLogger{log.New(io.MultiWriter(file, os.Stderr), "", 0), warningOperation}
|
|
errorLogger = customLogger{log.New(io.MultiWriter(file, os.Stderr), "", 0), errorOperation}
|
|
|
|
return file, nil
|
|
}
|
|
|
|
func main() {
|
|
file, err := initLogging()
|
|
if err != nil {
|
|
fmt.Printf("Error opening the provided logfile. %+v", err)
|
|
os.Exit(logfileNotOpenedError)
|
|
}
|
|
//TODO: The file won't open if init logging throws an error, but file.close can also
|
|
//have errors related to disk writing delays. Will update with more robust error handling
|
|
//but for now this works well enough
|
|
defer file.Close()
|
|
|
|
extensionMrSeq, environmentMrSeq, err = sequence.GetMostRecentSequenceNumber()
|
|
if err != nil {
|
|
warningLogger.Printf("Error getting sequence number %+v", err)
|
|
extensionMrSeq = -1
|
|
environmentMrSeq = -1
|
|
}
|
|
infoLogger.Printf("Extension MrSeq: %d, Environment MrSeq: %d", extensionMrSeq, environmentMrSeq)
|
|
|
|
// Command line flags that are currently supported
|
|
commandStringPtr := flag.String("command", "", "Valid commands are install, enable, update, disable and uninstall. Usage: --command=install")
|
|
parseJSONPtr := flag.String("jsonfile", "", "Path to the JSON file loction. Usage --jsonfile=\"test.json\"")
|
|
|
|
// Trigger parsing of the command flag and then run the corresponding command
|
|
flag.Parse()
|
|
|
|
err = parseJSON(*parseJSONPtr)
|
|
if err != nil {
|
|
errorLogger.Printf("Error parsing provided JSON file: %+v", err)
|
|
os.Exit(jsonParsingError)
|
|
}
|
|
|
|
switch *commandStringPtr {
|
|
case "disable":
|
|
disable()
|
|
case "uninstall":
|
|
uninstall()
|
|
case "install":
|
|
install()
|
|
case "enable":
|
|
enable()
|
|
case "update":
|
|
update()
|
|
case "":
|
|
warningLogger.Println("No --command flag provided")
|
|
os.Exit(commandNotFoundError)
|
|
default:
|
|
warningLogger.Printf("Command \"%s\" not recognized", *commandStringPtr)
|
|
os.Exit(commandNotFoundError)
|
|
}
|
|
}
|