Skip to content
Merged
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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ toolchain go1.24.5

require (
github.com/Azure/azure-extension-foundation v0.0.0-20250620154556-caff9e3c3c5c
github.com/Azure/azure-extension-platform v0.0.0-20240610175536-404c704f82f8
github.com/Azure/azure-extension-platform v0.0.0-20250107200156-aa20f765d49f
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ github.com/Azure/azure-extension-foundation v0.0.0-20250620154556-caff9e3c3c5c h
github.com/Azure/azure-extension-foundation v0.0.0-20250620154556-caff9e3c3c5c/go.mod h1:sNC6lMTUkXwjrQ+nttr6GXhDfvSGT7t3UDq30BEYzu8=
github.com/Azure/azure-extension-platform v0.0.0-20240610175536-404c704f82f8 h1:4AgLx0eXWAzh4nL7eBzwxoQaZEk5Hp2Ilq33YwYzEos=
github.com/Azure/azure-extension-platform v0.0.0-20240610175536-404c704f82f8/go.mod h1:nEQQIC3RKmMnpdc+RakYHIdu556jdcHv67ML8PdsQeQ=
github.com/Azure/azure-extension-platform v0.0.0-20250107200156-aa20f765d49f h1:ddsUz/suc9txCMz/xWOslqNMvzhbWFMTflUrbcMNoSw=
github.com/Azure/azure-extension-platform v0.0.0-20250107200156-aa20f765d49f/go.mod h1:0458BvQsi5ch6kn+KZtI5m88Z3L9UFXdoY1+6nKdivY=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M=
Expand Down
148 changes: 117 additions & 31 deletions internal/cmds/cmds.go

Large diffs are not rendered by default.

66 changes: 45 additions & 21 deletions internal/cmds/cmds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import (
"strings"
"testing"

"github.com/Azure/azure-extension-platform/pkg/extensionevents"
"github.com/Azure/azure-extension-platform/pkg/handlerenv"
"github.com/Azure/azure-extension-platform/pkg/logging"
"github.com/Azure/run-command-handler-linux/internal/constants"
"github.com/Azure/run-command-handler-linux/internal/files"
"github.com/Azure/run-command-handler-linux/internal/handlersettings"
Expand Down Expand Up @@ -74,7 +77,15 @@ func Test_CopyMrseqFiles_MrseqFilesAreCopied(t *testing.T) {
os.Create(filepath.Join(previousStatusDirectory, "ABCD.1.status"))
os.Create(filepath.Join(previousStatusDirectory, "abc.cs")) // this should not be copied to currentExtensionVersionDirectory

err = CopyStateForUpdate(log.NewContext(log.NewNopLogger()))
tempDir, _ := os.MkdirTemp("", "deletecmd")
defer os.RemoveAll(tempDir)
handlerEnvironment := handlerenv.HandlerEnvironment{
EventsFolder: tempDir,
}

extensionLogger := logging.New(nil)
extensionEventManager := extensionevents.New(extensionLogger, &handlerEnvironment)
err = CopyStateForUpdate(log.NewContext(log.NewNopLogger()), extensionEventManager)
require.Nil(t, err)

files, _ = ioutil.ReadDir(currentExtensionVersionDirectory)
Expand Down Expand Up @@ -178,23 +189,24 @@ func Test_checkAndSaveSeqNum(t *testing.T) {

func Test_update_e2e_cmd(t *testing.T) {
tempDir, _ := os.MkdirTemp("", "deletecmd")
defer os.RemoveAll(tempDir)

DataDir, _ = os.MkdirTemp("", "datadir")
defer os.RemoveAll(DataDir)

oldVersionDirectory := filepath.Join(tempDir, "Microsoft.CPlat.Core.RunCommandHandlerLinux-1.3.8")
newVersionDirectory := filepath.Join(tempDir, "Microsoft.CPlat.Core.RunCommandHandlerLinux-1.3.9")
err := os.Mkdir(oldVersionDirectory, 0755)
require.Nil(t, err, "Could not create old version subdirectory")
err = os.Mkdir(newVersionDirectory, 0755)
require.Nil(t, err, "Could not create new version subdirectory")
oldStatusPath := filepath.Join(oldVersionDirectory, constants.StatusFileDirectory)
err = os.Mkdir(oldStatusPath, 0755)
require.Nil(t, err, "Could not create old version status subdirectory")
newStatusPath := filepath.Join(newVersionDirectory, constants.StatusFileDirectory)
err = os.Mkdir(newStatusPath, 0755)
require.Nil(t, err, "Could not create new version status subdirectory")
oldStatusPath := create_folder(t, oldVersionDirectory, constants.StatusFileDirectory)
newStatusPath := create_folder(t, newVersionDirectory, constants.StatusFileDirectory)
oldEventsPath := create_folder(t, oldVersionDirectory, constants.ExtensionEventsDirectory)
newEventsPath := create_folder(t, newVersionDirectory, constants.ExtensionEventsDirectory)

fakeEnv := types.HandlerEnvironment{}
fakeEnv.HandlerEnvironment.ConfigFolder = oldVersionDirectory
fakeEnv.HandlerEnvironment.StatusFolder = oldStatusPath
update_handler_env(&fakeEnv, oldStatusPath, oldVersionDirectory, oldEventsPath)

// We start on the old version
os.Setenv(constants.ExtensionPathEnvName, oldVersionDirectory)
Expand All @@ -213,8 +225,7 @@ func Test_update_e2e_cmd(t *testing.T) {
os.Setenv(constants.ExtensionVersionEnvName, "1.3.9")
os.Setenv(constants.ExtensionPathEnvName, newVersionDirectory)
os.Setenv(constants.ExtensionVersionUpdatingFromEnvName, "1.3.8")
fakeEnv.HandlerEnvironment.StatusFolder = newStatusPath
fakeEnv.HandlerEnvironment.ConfigFolder = newVersionDirectory
update_handler_env(&fakeEnv, newStatusPath, newVersionDirectory, newEventsPath)
update_handler(t, fakeEnv, tempDir)

// Now, WALA will uninstall the old extension
Expand All @@ -230,23 +241,24 @@ func Test_update_e2e_cmd(t *testing.T) {

func Test_udpate_e2e_problematic_version(t *testing.T) {
tempDir, _ := os.MkdirTemp("", "deletecmd")
defer os.RemoveAll(tempDir)

DataDir, _ = os.MkdirTemp("", "datadir")
defer os.RemoveAll(DataDir)

oldVersionDirectory := filepath.Join(tempDir, "Microsoft.CPlat.Core.RunCommandHandlerLinux-1.3.17")
newVersionDirectory := filepath.Join(tempDir, "Microsoft.CPlat.Core.RunCommandHandlerLinux-1.3.18")
err := os.Mkdir(oldVersionDirectory, 0755)
require.Nil(t, err, "Could not create old version subdirectory")
err = os.Mkdir(newVersionDirectory, 0755)
require.Nil(t, err, "Could not create new version subdirectory")
oldStatusPath := filepath.Join(oldVersionDirectory, constants.StatusFileDirectory)
err = os.Mkdir(oldStatusPath, 0755)
require.Nil(t, err, "Could not create old version status subdirectory")
newStatusPath := filepath.Join(newVersionDirectory, constants.StatusFileDirectory)
err = os.Mkdir(newStatusPath, 0755)
require.Nil(t, err, "Could not create new version status subdirectory")
oldStatusPath := create_folder(t, oldVersionDirectory, constants.StatusFileDirectory)
newStatusPath := create_folder(t, newVersionDirectory, constants.StatusFileDirectory)
oldEventsPath := create_folder(t, oldVersionDirectory, constants.ExtensionEventsDirectory)
newEventsPath := create_folder(t, newVersionDirectory, constants.ExtensionEventsDirectory)

fakeEnv := types.HandlerEnvironment{}
fakeEnv.HandlerEnvironment.ConfigFolder = oldVersionDirectory
fakeEnv.HandlerEnvironment.StatusFolder = oldStatusPath
update_handler_env(&fakeEnv, oldStatusPath, oldVersionDirectory, oldEventsPath)

// We start on the old version
os.Setenv(constants.ExtensionPathEnvName, oldVersionDirectory)
Expand Down Expand Up @@ -280,8 +292,7 @@ func Test_udpate_e2e_problematic_version(t *testing.T) {
os.Setenv(constants.ExtensionVersionEnvName, "1.3.18")
os.Setenv(constants.ExtensionPathEnvName, newVersionDirectory)
os.Setenv(constants.ExtensionVersionUpdatingFromEnvName, "1.3.17")
fakeEnv.HandlerEnvironment.StatusFolder = newStatusPath
fakeEnv.HandlerEnvironment.ConfigFolder = newVersionDirectory
update_handler_env(&fakeEnv, newStatusPath, newVersionDirectory, newEventsPath)
update_handler(t, fakeEnv, tempDir)

// Now, WALA will uninstall the old extension
Expand All @@ -301,6 +312,19 @@ func Test_udpate_e2e_problematic_version(t *testing.T) {
enable_extension(t, fakeEnv, newVersionDirectory, "stubbornChipmunk", true, 1)
}

func create_folder(t *testing.T, versionDirectory string, folderName string) string {
folderPath := filepath.Join(versionDirectory, folderName)
err := os.Mkdir(folderPath, 0755)
require.Nil(t, err, "Could not create folder "+folderName)
return folderPath
}

func update_handler_env(fakeEnv *types.HandlerEnvironment, statusFolder string, configFolder string, eventsFolder string) {
fakeEnv.HandlerEnvironment.StatusFolder = statusFolder
fakeEnv.HandlerEnvironment.ConfigFolder = configFolder
fakeEnv.HandlerEnvironment.EventsFolder = eventsFolder
}

func install_handler(t *testing.T, fakeEnv types.HandlerEnvironment, tempDir string) {
generic_handler_call(t, fakeEnv, tempDir, "install", types.CmdInstallTemplate, CmdInstall)
}
Expand Down
5 changes: 5 additions & 0 deletions internal/commandProcessor/commandProcessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"
"time"

"github.com/Azure/azure-extension-platform/pkg/handlerenv"
"github.com/Azure/azure-extension-platform/pkg/logging"
"github.com/Azure/run-command-handler-linux/internal/constants"
"github.com/Azure/run-command-handler-linux/internal/handlersettings"
Expand All @@ -19,6 +20,10 @@ import (
"github.com/pkg/errors"
)

var (
handlerEnvironmentGetter func(name, version string) (he *handlerenv.HandlerEnvironment, _ error) = handlerenv.GetHandlerEnvironment
)

func ProcessImmediateHandlerCommand(cmd types.Cmd, hs handlersettings.HandlerSettingsFile, extensionName string, seqNum int) error {
ctx := initializeLogger(cmd)
ctx = ctx.With("extensionName", extensionName)
Expand Down
2 changes: 2 additions & 0 deletions internal/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const (

StatusFileDirectory = "status"

ExtensionEventsDirectory = "events"

StatusFileExtension = ".status"

// The directory where the immediate run command status that have reached the terminal status are stored.
Expand Down
47 changes: 37 additions & 10 deletions internal/immediatecmds/immediatecmds.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package immediatecmds

import (
"fmt"

"github.com/Azure/azure-extension-platform/pkg/extensionevents"
"github.com/Azure/run-command-handler-linux/internal/constants"
"github.com/Azure/run-command-handler-linux/internal/handlersettings"
"github.com/Azure/run-command-handler-linux/internal/service"
Expand All @@ -11,42 +14,53 @@ import (

// Updates the service definition if any immediate run command service exists.
// The action is skipped if the service has already been upgraded.
func Update(ctx *log.Context, h types.HandlerEnvironment, extName string, seqNum int) (int, error) {
func Update(ctx *log.Context, h types.HandlerEnvironment, extName string, seqNum int, extensionEvents *extensionevents.ExtensionEventManager) (int, error) {
ctx.Log("message", "updating immediate run command")
isInstalled, err := service.IsInstalled(ctx)
if err != nil {
errMessage := fmt.Sprintf("Failed to check if any runcommand service is installed: %v", err)
extensionEvents.LogErrorEvent("immediateupdate", errMessage)
return constants.ExitCode_CreateDataDirectoryFailed, errors.Wrap(err, "failed to check if any runcommand service is installed")
}

if isInstalled {
err = service.Register(ctx)
err = service.Register(ctx, extensionEvents)
if err != nil {
errMessage := fmt.Sprintf("Failed to upgrade run command service: %v", err)
extensionEvents.LogErrorEvent("immediateupdate", errMessage)
return constants.ExitCode_UpgradeInstalledServiceFailed, errors.Wrap(err, "failed to upgrade run command service")
}
}

return constants.ExitCode_Okay, nil
}

func Disable(ctx *log.Context, h types.HandlerEnvironment, extName string, seqNum int) (int, error) {
func Disable(ctx *log.Context, h types.HandlerEnvironment, extName string, seqNum int, extensionEvents *extensionevents.ExtensionEventManager) (int, error) {
isInstalled, err := service.IsInstalled(ctx)
if err != nil {
errMessage := fmt.Sprintf("Failed to check if runcommand service is installed: %v", err)
extensionEvents.LogErrorEvent("immediatedisable", errMessage)
return constants.ExitCode_DisableInstalledServiceFailed, errors.Wrap(err, "failed to check if runcommand service is installed")
}

if isInstalled {
isEnabled, err := service.IsEnabled(ctx)
if err != nil {
errMessage := fmt.Sprintf("Failed to check if service is enabled: %v", err)
extensionEvents.LogErrorEvent("immediatedisable", errMessage)
return constants.ExitCode_InstallServiceFailed, errors.Wrap(err, "failed to check if service is enabled")
}

if isEnabled {
err := service.Disable(ctx)
err := service.Disable(ctx, extensionEvents)
if err != nil {
errMessage := fmt.Sprintf("Failed to disable run command service: %v", err)
extensionEvents.LogErrorEvent("immediatedisable", errMessage)
return constants.ExitCode_DisableInstalledServiceFailed, errors.Wrap(err, "failed to disable run command service")
}
} else {
ctx.Log("message", "Service installed but already got disabled. Skipping request to disable")
extensionEvents.LogInformationalEvent("immediatedisable", "Service installed but already got disabled. Skipping request to disable")
}
}

Expand All @@ -58,51 +72,64 @@ func Install() (int, error) {
return constants.ExitCode_Okay, nil
}

func Uninstall(ctx *log.Context, h types.HandlerEnvironment, extName string, seqNum int) (int, error) {
func Uninstall(ctx *log.Context, h types.HandlerEnvironment, extName string, seqNum int, extensionEvents *extensionevents.ExtensionEventManager) (int, error) {
ctx.Log("message", "proceeding to uninstall immediate run command")
isInstalled, err := service.IsInstalled(ctx)
if err != nil {
errMessage := fmt.Sprintf("Failed to check if runcommand service is installed: %v", err)
extensionEvents.LogErrorEvent("immediatedisable", errMessage)
return constants.ExitCode_RemoveDataDirectoryFailed, errors.Wrap(err, "failed to check if runcommand service is installed")
}

if isInstalled {
error := service.DeRegister(ctx)
error := service.DeRegister(ctx, extensionEvents)
if error != nil {
errMessage := fmt.Sprintf("Failed to uninstall run command service: %v", error)
extensionEvents.LogErrorEvent("immediatedisable", errMessage)
return constants.ExitCode_UninstallInstalledServiceFailed, errors.Wrap(err, "failed to uninstall run command service")
}
}
return constants.ExitCode_Okay, nil
}

func Enable(ctx *log.Context, h types.HandlerEnvironment, extName string, seqNum int, cfg handlersettings.HandlerSettings) (int, error) {
func Enable(ctx *log.Context, h types.HandlerEnvironment, extName string, seqNum int, cfg handlersettings.HandlerSettings, extensionEvents *extensionevents.ExtensionEventManager) (int, error) {
// If installService == true, then install RunCommand as a service
if cfg.InstallAsService() {
isInstalled, err2 := service.IsInstalled(ctx)
if err2 != nil {
ctx.Log("message", "could not check if service is already installed. Proceeding to overwrite configuration file to make sure it gets installed.")
extensionEvents.LogErrorEvent("immediateenable", "could not check if service is already installed. Proceeding to overwrite configuration file to make sure it gets installed.")
}

if !isInstalled {
err3 := service.Register(ctx)
err3 := service.Register(ctx, extensionEvents)
if err3 != nil {
errMessage := fmt.Sprintf("Failed to install RunCommand as a service: %v", err3)
extensionEvents.LogErrorEvent("immediateenable", errMessage)
return constants.ExitCode_InstallServiceFailed, errors.Wrap(err3, "failed to install RunCommand as a service")
}
} else {
isEnabled, err3 := service.IsEnabled(ctx)
if err3 != nil {
errMessage := fmt.Sprintf("Failed to check if service is already enabled: %v", err3)
extensionEvents.LogErrorEvent("immediateenable", errMessage)
return constants.ExitCode_InstallServiceFailed, errors.Wrap(err3, "failed to check if service is already enabled")
}

if !isEnabled {
err4 := service.Enable(ctx)
err4 := service.Enable(ctx, extensionEvents)

if err4 != nil {
errMessage := fmt.Sprintf("Failed to enable service: %v", err4)
extensionEvents.LogErrorEvent("immediateenable", errMessage)
return constants.ExitCode_InstallServiceFailed, errors.Wrap(err4, "failed to enable service")
}

err5 := service.Start(ctx)
err5 := service.Start(ctx, extensionEvents)

if err5 != nil {
errMessage := fmt.Sprintf("Failed to start service: %v", err5)
extensionEvents.LogErrorEvent("immediateenable", errMessage)
return constants.ExitCode_InstallServiceFailed, errors.Wrap(err5, "failed to start service")
}
}
Expand Down
Loading