-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #91 from d-strobel/feat/dhcp-server-functions
Feat/dhcp server functions
- Loading branch information
Showing
15 changed files
with
1,238 additions
and
90 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package parsing | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
) | ||
|
||
// PwshTimespanString returns a string representation of a time.Duration that can be used in a PowerShell command. | ||
// The returned string is in the format "$(New-TimeSpan -Days <days> -Hours <hours> -Minutes <minutes> -Seconds <seconds>)". | ||
func PwshTimespanString(d time.Duration) string { | ||
return fmt.Sprintf( | ||
"$(New-TimeSpan -Days %d -Hours %d -Minutes %d -Seconds %d)", | ||
int32(d.Hours())/24, | ||
int32(d.Hours())%24, | ||
int32(d.Minutes())%60, | ||
int32(d.Seconds())%60, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package parsing | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestPwshTimespanString(t *testing.T) { | ||
t.Parallel() | ||
|
||
tcs := []struct { | ||
description string | ||
inputDuration string | ||
expectedString string | ||
}{ | ||
{ | ||
"duration of 0", | ||
"0h", | ||
"$(New-TimeSpan -Days 0 -Hours 0 -Minutes 0 -Seconds 0)", | ||
}, | ||
{ | ||
"duration with 2 days 30 minutes and 15 seconds", | ||
"48h30m15s", | ||
"$(New-TimeSpan -Days 2 -Hours 0 -Minutes 30 -Seconds 15)", | ||
}, | ||
{ | ||
"duration with 1 day 2 hours 30 minutes and 15 seconds", | ||
"26h30m15s", | ||
"$(New-TimeSpan -Days 1 -Hours 2 -Minutes 30 -Seconds 15)", | ||
}, | ||
} | ||
|
||
for _, tc := range tcs { | ||
t.Run(tc.description, func(t *testing.T) { | ||
d, err := time.ParseDuration(tc.inputDuration) | ||
assert.NoError(t, err) | ||
actualString := PwshTimespanString(d) | ||
assert.Equal(t, tc.expectedString, actualString) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// Package dhcp provides a Go library for handling Windows DHCP Server resources. | ||
// The functions are related to the Powershell DhcpServer cmdlets provided by Windows. | ||
// https://learn.microsoft.com/en-us/powershell/module/dhcpserver/?view=windowsserver2022-ps | ||
package dhcp | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"net/netip" | ||
|
||
"errors" | ||
|
||
"github.com/d-strobel/gowindows/connection" | ||
"github.com/d-strobel/gowindows/parsing" | ||
) | ||
|
||
// dhcp is a type constraint for the run function, ensuring it works with specific types. | ||
type dhcp interface { | ||
scopeObject | ||
} | ||
|
||
// scopeObject is used to unmarshal the JSON output of a scope object. | ||
type scopeObject struct { | ||
Name string `json:"Name"` | ||
Description string `json:"Description"` | ||
ScopeId scopeId `json:"ScopeId"` | ||
StartRange startRange `json:"StartRange"` | ||
EndRange endRange `json:"EndRange"` | ||
SubnetMask subnetMask `json:"SubnetMask"` | ||
State string `json:"State"` | ||
MaxBootpClients uint32 `json:"MaxBootpClients"` | ||
ActivatePolicies bool `json:"ActivatePolicies"` | ||
NapEnable bool `json:"NapEnable"` | ||
NapProfile string `json:"NapProfile"` | ||
Delay uint16 `json:"Delay"` | ||
LeaseDuration parsing.CimTimeDuration `json:"LeaseDuration"` | ||
} | ||
type scopeId struct { | ||
Address netip.Addr `json:"IPAddressToString"` | ||
} | ||
type startRange struct { | ||
Address netip.Addr `json:"IPAddressToString"` | ||
} | ||
type endRange struct { | ||
Address netip.Addr `json:"IPAddressToString"` | ||
} | ||
type subnetMask struct { | ||
Address netip.Addr `json:"IPAddressToString"` | ||
} | ||
|
||
// Client represents a client for handling DHCP server functions. | ||
type Client struct { | ||
// Connection represents a connection.Connection object. | ||
Connection connection.Connection | ||
|
||
// decodeCliXmlErr represents a function that decodes a CLIXML error and returns aa human readable string. | ||
decodeCliXmlErr func(string) (string, error) | ||
} | ||
|
||
// NewClient returns a new instance of the Client. | ||
func NewClient(conn connection.Connection) *Client { | ||
return NewClientWithParser(conn, parsing.DecodeCliXmlErr) | ||
} | ||
|
||
// NewClientWithParser returns a new instance of the Client. | ||
// It requires a connection and parsing as input parameters. | ||
func NewClientWithParser(conn connection.Connection, parsing func(string) (string, error)) *Client { | ||
return &Client{Connection: conn, decodeCliXmlErr: parsing} | ||
} | ||
|
||
// run runs a PowerShell command against a Windows system, handles the command results, | ||
// and unmarshals the output into a local object type. | ||
func run[T dhcp](ctx context.Context, c *Client, cmd string, t *T) error { | ||
// Run the command | ||
result, err := c.Connection.RunWithPowershell(ctx, cmd) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Handle stderr | ||
if result.StdErr != "" { | ||
stderr, err := c.decodeCliXmlErr(result.StdErr) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return errors.New(stderr) | ||
} | ||
|
||
if result.StdOut == "" { | ||
return nil | ||
} | ||
|
||
// Unmarshal stdout | ||
if err = json.Unmarshal([]byte(result.StdOut), &t); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package dhcp | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/d-strobel/gowindows/connection" | ||
mockConnection "github.com/d-strobel/gowindows/connection/mocks" | ||
"github.com/stretchr/testify/suite" | ||
) | ||
|
||
// Unit test suite for all dhcp functions | ||
type DhcpServerUnitTestSuite struct { | ||
suite.Suite | ||
} | ||
|
||
// Run all dhcp unit tests | ||
func TestDhcpServerUnitTestSuite(t *testing.T) { | ||
suite.Run(t, &DhcpServerUnitTestSuite{}) | ||
} | ||
|
||
func (suite *DhcpServerUnitTestSuite) TestNewClient() { | ||
suite.Run("should return a new dhcp client", func() { | ||
mockConn := mockConnection.NewMockConnection(suite.T()) | ||
mockDecodeCliXmlErr := func(s string) (string, error) { return "", nil } | ||
actualClient := NewClientWithParser(mockConn, mockDecodeCliXmlErr) | ||
expectedClient := &Client{Connection: mockConn, decodeCliXmlErr: mockDecodeCliXmlErr} | ||
suite.IsType(expectedClient, actualClient) | ||
suite.Equal(expectedClient.Connection, actualClient.Connection) | ||
}) | ||
} | ||
|
||
func (suite *DhcpServerUnitTestSuite) TestDhcpRun() { | ||
suite.T().Parallel() | ||
|
||
suite.Run("should return an unmarshalled scope object", func() { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
mockConn := mockConnection.NewMockConnection(suite.T()) | ||
c := &Client{ | ||
Connection: mockConn, | ||
decodeCliXmlErr: func(s string) (string, error) { return "", nil }, | ||
} | ||
cmd := "Get-DhcpServerv4Scope -ScopeId '192.168.10.0' | ConvertTo-Json -Compress" | ||
mockConn.EXPECT(). | ||
RunWithPowershell(ctx, cmd). | ||
Return(connection.CmdResult{StdOut: scopeV4Json}, nil) | ||
var o scopeObject | ||
err := run(ctx, c, cmd, &o) | ||
suite.NoError(err) | ||
suite.Equal(expectedScopeObject, o) | ||
}) | ||
} |
Oops, something went wrong.