Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removed os.Exec'ing code #2961

Closed
Closed
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
46 changes: 46 additions & 0 deletions platform/osRegistryInterface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//go:build windows
// +build windows
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module requires at least Go 1.18 to build, so we no longer need to add // +build windows. The //go:build pragma is the only one needed.


package platform

import "golang.org/x/sys/windows/registry"

// Registry interface for interacting with the Windows registry
type Registry interface {
OpenKey(k registry.Key, path string, access uint32) (RegistryKey, error)
}

// RegistryKey interface to represent an open registry key
type RegistryKey interface {
GetStringValue(name string) (string, uint32, error)
SetStringValue(name, value string) error
Close() error
}

type WindowsRegistry struct{}

// WindowsRegistryKey implements the RegistryKey interface
type WindowsRegistryKey struct {
key registry.Key
}

func (r *WindowsRegistry) OpenKey(k registry.Key, path string, access uint32) (RegistryKey, error) {

Check failure on line 27 in platform/osRegistryInterface.go

View workflow job for this annotation

GitHub Actions / Lint (1.21.x, windows-latest)

unused-parameter: parameter 'k' seems to be unused, consider removing or renaming it as _ (revive)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

k is unused here--I don't think that's intentional. Granted, we're probably always going to use HKLM, but who knows? If we're making a generic wrapper, I'd like to be able to, say, edit some keys in HKCU.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as someone with no understanding of win reg, you could be making these up and I would have no idea 😂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rbtr this is cursed knowledge from working summers at my high school playing student sysadmin :-P The Norton Ghosts still haunt me.

key, err := registry.OpenKey(registry.LOCAL_MACHINE, path, access)
if err != nil {
return nil, err
}
return &WindowsRegistryKey{key}, nil
}

func (k *WindowsRegistryKey) GetStringValue(name string) (val string, valtype uint32, err error) {
value, valType, err := k.key.GetStringValue(name)
return value, valType, err
Comment on lines +36 to +37
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This simplifies to

Suggested change
value, valType, err := k.key.GetStringValue(name)
return value, valType, err
return k.key.GetStringValue(name)

}

func (k *WindowsRegistryKey) SetStringValue(name, value string) error {
return k.key.SetStringValue(name, value)
}

func (k *WindowsRegistryKey) Close() error {
return k.key.Close()
}
93 changes: 68 additions & 25 deletions platform/os_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
"github.com/pkg/errors"
"go.uber.org/zap"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/mgr"
)

const (
Expand Down Expand Up @@ -61,24 +64,12 @@
// for vlan tagged arp requests
SDNRemoteArpMacAddress = "12-34-56-78-9a-bc"

// Command to get SDNRemoteArpMacAddress registry key
GetSdnRemoteArpMacAddressCommand = "(Get-ItemProperty " +
"-Path HKLM:\\SYSTEM\\CurrentControlSet\\Services\\hns\\State -Name SDNRemoteArpMacAddress).SDNRemoteArpMacAddress"

// Command to set SDNRemoteArpMacAddress registry key
SetSdnRemoteArpMacAddressCommand = "Set-ItemProperty " +
"-Path HKLM:\\SYSTEM\\CurrentControlSet\\Services\\hns\\State -Name SDNRemoteArpMacAddress -Value \"12-34-56-78-9a-bc\""

// Command to check if system has hns state path or not
CheckIfHNSStatePathExistsCommand = "Test-Path " +
"-Path HKLM:\\SYSTEM\\CurrentControlSet\\Services\\hns\\State"

// Command to fetch netadapter and pnp id
//TODO can we replace this (and things in endpoint_windows) with "golang.org/x/sys/windows"

Check failure on line 68 in platform/os_windows.go

View workflow job for this annotation

GitHub Actions / Lint (1.21.x, windows-latest)

commentFormatting: put a space between `//` and comment text (gocritic)
//var adapterInfo windows.IpAdapterInfo
//var bufferSize uint32 = uint32(unsafe.Sizeof(adapterInfo))
GetMacAddressVFPPnpIDMapping = "Get-NetAdapter | Select-Object MacAddress, PnpDeviceID| Format-Table -HideTableHeaders"

// Command to restart HNS service
RestartHnsServiceCommand = "Restart-Service -Name hns"

// Interval between successive checks for mellanox adapter's PriorityVLANTag value
defaultMellanoxMonitorInterval = 30 * time.Second

Expand Down Expand Up @@ -257,32 +248,39 @@
}

// SetSdnRemoteArpMacAddress sets the regkey for SDNRemoteArpMacAddress needed for multitenancy if hns is enabled
func SetSdnRemoteArpMacAddress(execClient ExecClient) error {
exists, err := execClient.ExecutePowershellCommand(CheckIfHNSStatePathExistsCommand)
func SetSdnRemoteArpMacAddress(reg Registry) error {
key, err := reg.OpenKey(registry.LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\hns\\State", registry.READ|registry.SET_VALUE)
if err != nil {
if err == registry.ErrNotExist {
log.Printf("hns state path does not exist, skip setting SdnRemoteArpMacAddress")
return nil
}
errMsg := fmt.Sprintf("Failed to check the existent of hns state path due to error %s", err.Error())
log.Printf(errMsg)
return errors.Errorf(errMsg)
}
if strings.EqualFold(exists, "false") {
log.Printf("hns state path does not exist, skip setting SdnRemoteArpMacAddress")
return nil
}

if sdnRemoteArpMacAddressSet == false {
result, err := execClient.ExecutePowershellCommand(GetSdnRemoteArpMacAddressCommand)

//Was (Get-ItemProperty -Path HKLM:\\SYSTEM\\CurrentControlSet\\Services\\hns\\State -Name SDNRemoteArpMacAddress).SDNRemoteArpMacAddress"

Check failure on line 265 in platform/os_windows.go

View workflow job for this annotation

GitHub Actions / Lint (1.21.x, windows-latest)

commentFormatting: put a space between `//` and comment text (gocritic)
result, _, err := key.GetStringValue("SDNRemoteArpMacAddress")
if err != nil {
return err
}

// Set the reg key if not already set or has incorrect value
if result != SDNRemoteArpMacAddress {
if _, err = execClient.ExecutePowershellCommand(SetSdnRemoteArpMacAddressCommand); err != nil {

//was "Set-ItemProperty -Path HKLM:\\SYSTEM\\CurrentControlSet\\Services\\hns\\State -Name SDNRemoteArpMacAddress -Value \"12-34-56-78-9a-bc\""

Check failure on line 274 in platform/os_windows.go

View workflow job for this annotation

GitHub Actions / Lint (1.21.x, windows-latest)

commentFormatting: put a space between `//` and comment text (gocritic)

if err := key.SetStringValue("SDNRemoteArpMacAddress", SDNRemoteArpMacAddress); err != nil {
log.Printf("Failed to set SDNRemoteArpMacAddress due to error %s", err.Error())
return err
}

log.Printf("[Azure CNS] SDNRemoteArpMacAddress regKey set successfully. Restarting hns service.")
if _, err := execClient.ExecutePowershellCommand(RestartHnsServiceCommand); err != nil {

// was "Restart-Service -Name hns"
if err := restartService("hns"); err != nil {
log.Printf("Failed to Restart HNS Service due to error %s", err.Error())
return err
}
Expand All @@ -294,6 +292,50 @@
return nil
}

// straight out of chat gpt
func restartService(serviceName string) error {
// Connect to the service manager
m, err := mgr.Connect()
if err != nil {
return fmt.Errorf("could not connect to service manager: %v", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use errors.Wrap

}
defer m.Disconnect()

Check failure on line 302 in platform/os_windows.go

View workflow job for this annotation

GitHub Actions / Lint (1.21.x, windows-latest)

Error return value of `m.Disconnect` is not checked (errcheck)

// Open the service by name
service, err := m.OpenService(serviceName)
if err != nil {
return fmt.Errorf("could not access service: %v", err)
}
defer service.Close()

// Stop the service
_, err = service.Control(svc.Stop)
if err != nil {
return fmt.Errorf("could not stop service: %v", err)
}

// Wait for the service to stop
status, err := service.Query()
if err != nil {
return fmt.Errorf("could not query service status: %v", err)
}
for status.State != svc.Stopped {
time.Sleep(500 * time.Millisecond)
status, err = service.Query()
if err != nil {
return fmt.Errorf("could not query service status: %v", err)
}
}
Comment on lines +322 to +328
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should consult a context.Context for cancelation so that it will not run indefinitely.


// Start the service again
err = service.Start()
if err != nil {
return fmt.Errorf("could not start service: %v", err)
}

return nil
}

func HasMellanoxAdapter() bool {
m := &mellanox.Mellanox{}
return hasNetworkAdapter(m)
Expand Down Expand Up @@ -364,6 +406,7 @@
pidstr = strings.Trim(pidstr, "\r\n")
cmd := fmt.Sprintf("Get-Process -Id %s|Format-List", pidstr)
p := NewExecClient(nil)
//TODO not riemovign this because it seems to only be called in test?

Check failure on line 409 in platform/os_windows.go

View workflow job for this annotation

GitHub Actions / Lint (1.21.x, windows-latest)

commentFormatting: put a space between `//` and comment text (gocritic)
out, err := p.ExecutePowershellCommand(cmd)
if err != nil {
log.Printf("Process is not running. Output:%v, Error %v", out, err)
Expand Down
86 changes: 68 additions & 18 deletions platform/os_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,66 @@
"context"
"errors"
"os/exec"
"strings"
"testing"
"time"

"github.com/Azure/azure-container-networking/platform/windows/adapter/mocks"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/sys/windows/registry"
)

var errTestFailure = errors.New("test failure")

// MockRegistry is a mock implementation of the Registry interface
type MockRegistry struct {
Keys map[string]*MockRegistryKey
}

// OpenKey opens a mock registry key.
func (r *MockRegistry) OpenKey(k registry.Key, path string, access uint32) (RegistryKey, error) {

Check failure on line 25 in platform/os_windows_test.go

View workflow job for this annotation

GitHub Actions / Lint (1.21.x, windows-latest)

unused-parameter: parameter 'k' seems to be unused, consider removing or renaming it as _ (revive)
// Directly check if the key exists in the mock registry by its path
if key, exists := r.Keys[path]; exists {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Traditionally, the Go community writes the second parameter here as ok.

Suggested change
if key, exists := r.Keys[path]; exists {
if key, ok := r.Keys[path]; ok {

return key, nil
}
return nil, errors.New("key does not exist")
}

// MockRegistryKey is a mock implementation of the RegistryKey interface
type MockRegistryKey struct {
Values map[string]string
}

func (k *MockRegistryKey) GetStringValue(name string) (string, uint32, error) {

Check failure on line 38 in platform/os_windows_test.go

View workflow job for this annotation

GitHub Actions / Lint (1.21.x, windows-latest)

unnamedResult: consider giving a name to these results (gocritic)
if value, exists := k.Values[name]; exists {
return value, registry.SZ, nil
}
return "", registry.SZ, registry.ErrNotExist
}

func (k *MockRegistryKey) SetStringValue(name, value string) error {
k.Values[name] = value
return nil
}

func (k *MockRegistryKey) Close() error {
return nil
}
Comment on lines +50 to +52
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be incorrect to not close a key? It might be worth adding some bookkeeping in here to make sure it's getting called if so.


func initMockRegistry() *MockRegistry {
mockRegistry := &MockRegistry{
Keys: map[string]*MockRegistryKey{
`SOFTWARE\MockCompany\MockApp`: {
Values: map[string]string{
"MockValue": "MockData",
},
},
},
}
return mockRegistry
}

// Test if hasNetworkAdapter returns false on actual error or empty adapter name(an error)
func TestHasNetworkAdapterReturnsError(t *testing.T) {
ctrl := gomock.NewController(t)
Expand Down Expand Up @@ -116,34 +164,36 @@
}

func TestSetSdnRemoteArpMacAddress_hnsNotEnabled(t *testing.T) {
mockExecClient := NewMockExecClient(false)
//mockExecClient := NewMockExecClient(false)

Check failure on line 167 in platform/os_windows_test.go

View workflow job for this annotation

GitHub Actions / Lint (1.21.x, windows-latest)

commentFormatting: put a space between `//` and comment text (gocritic)
mockRegistry := initMockRegistry()
// testing skip setting SdnRemoteArpMacAddress when hns not enabled
mockExecClient.SetPowershellCommandResponder(func(_ string) (string, error) {
return "False", nil
})
err := SetSdnRemoteArpMacAddress(mockExecClient)
// mockExecClient.SetPowershellCommandResponder(func(_ string) (string, error) {
// return "False", nil
// })
Comment on lines +170 to +172
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to remove these. Dead code can be found and resurrected with git if necessary.

err := SetSdnRemoteArpMacAddress(mockRegistry)
assert.NoError(t, err)
assert.Equal(t, false, sdnRemoteArpMacAddressSet)

// testing the scenario when there is an error in checking if hns is enabled or not
mockExecClient.SetPowershellCommandResponder(func(_ string) (string, error) {
return "", errTestFailure
})
err = SetSdnRemoteArpMacAddress(mockExecClient)
// mockExecClient.SetPowershellCommandResponder(func(_ string) (string, error) {
// return "", errTestFailure
// })
err = SetSdnRemoteArpMacAddress(mockRegistry)
assert.ErrorAs(t, err, &errTestFailure)
assert.Equal(t, false, sdnRemoteArpMacAddressSet)
}

func TestSetSdnRemoteArpMacAddress_hnsEnabled(t *testing.T) {
mockExecClient := NewMockExecClient(false)
//mockExecClient := NewMockExecClient(false)

Check failure on line 187 in platform/os_windows_test.go

View workflow job for this annotation

GitHub Actions / Lint (1.21.x, windows-latest)

commentFormatting: put a space between `//` and comment text (gocritic)
mockRegistry := initMockRegistry()
// happy path
mockExecClient.SetPowershellCommandResponder(func(cmd string) (string, error) {
if strings.Contains(cmd, "Test-Path") {
return "True", nil
}
return "", nil
})
err := SetSdnRemoteArpMacAddress(mockExecClient)
// mockExecClient.SetPowershellCommandResponder(func(cmd string) (string, error) {
// if strings.Contains(cmd, "Test-Path") {
// return "True", nil
// }
// return "", nil
// })
err := SetSdnRemoteArpMacAddress(mockRegistry)
assert.NoError(t, err)
assert.Equal(t, true, sdnRemoteArpMacAddressSet)
// reset sdnRemoteArpMacAddressSet
Expand Down
Loading