-
Notifications
You must be signed in to change notification settings - Fork 5
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
Add File resource #22
Open
nyanhp
wants to merge
12
commits into
dsccommunity:main
Choose a base branch
from
nyanhp:fileresource
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
d4df7e5
Initial commit
nyanhp abdaa40
Sample added
nyanhp 6f731b5
add empty strings file
nyanhp 5a4f764
Update ScriptFileInfo
nyanhp bb021cf
update sample code
nyanhp d909af7
Move enums to individual files
nyanhp e4f8fd3
Add rudimentary tests
nyanhp 93265b1
FileSystemObject Integration tests updated
nyanhp 49a124c
Last attempt to fix integration tests
nyanhp 1b32e5a
Merge branch 'main' into fileresource
johlju fcc02c0
Fix style (instead of commenting)
johlju 54a453f
Fix style change bvug
johlju File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,383 @@ | ||
class FileSystemDscReason | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This class should be in a seperate file |
||
{ | ||
[DscProperty()] | ||
[System.String] | ||
$Code | ||
|
||
[DscProperty()] | ||
[System.String] | ||
$Phrase | ||
} | ||
|
||
<# | ||
.SYNOPSIS | ||
The File resource enables file system operations on Linux and Windows. | ||
With regards to parameters and globbing, it behaves like the Item and Content | ||
cmdlets. | ||
|
||
.PARAMETER DestinationPath | ||
The path to create/copy to. | ||
|
||
.PARAMETER SourcePath | ||
If data should be copied, the source path to copy from. | ||
|
||
.PARAMETER Ensure | ||
Indicates if destination should be created or removed. Values: Absent, Present. Default: Present. | ||
|
||
.PARAMETER Type | ||
The type of the object to create. Values: file, directory, symboliclink. Default: directory | ||
|
||
.PARAMETER Contents | ||
The file contents. Unused if type is directory | ||
|
||
.PARAMETER Checksum | ||
The type of checksum to use for copy operations. Values: md5, CreationTime, LastModifiedTime. Default: md5 | ||
|
||
.PARAMETER Recurse | ||
Indicates that recurse should be used if data is copied. | ||
|
||
.PARAMETER Force | ||
Indicates that folder structures should be created and existing files overwritten | ||
|
||
.PARAMETER Links | ||
Link behavior, currently not implemented. Values: follow, manage. Default: follow | ||
|
||
.PARAMETER Group | ||
Linux group name for chown, currently not implemented. | ||
|
||
.PARAMETER Mode | ||
Linux mode for chmod, currently not implemented. | ||
|
||
.PARAMETER Owner | ||
Linux owner name for chown, currently not implemented. | ||
|
||
.PARAMETER Encoding | ||
File encoding, used with Contents. Values: ASCII, Latin1, UTF7, UTF8, UTF32, BigEndianUnicode, Default, Unicode. Default: Default | ||
|
||
.PARAMETER IgnoreTrailingWhitespace | ||
Indicates that trailing whitespace should be ignored when comparing file contents. | ||
#> | ||
[DscResource()] | ||
class FileSystemObject | ||
{ | ||
[DscProperty(Key)] | ||
[System.String] | ||
$DestinationPath | ||
|
||
[DscProperty()] | ||
[System.String] | ||
$SourcePath | ||
|
||
[DscProperty()] | ||
[Ensure] | ||
$Ensure = [Ensure]::Present | ||
|
||
[DscProperty()] | ||
[ObjectType] | ||
$Type = [ObjectType]::Directory | ||
|
||
[DscProperty()] | ||
[System.String] | ||
$Contents | ||
|
||
[DscProperty()] | ||
[ChecksumType] | ||
$Checksum = [ChecksumType]::MD5 | ||
|
||
[DscProperty()] | ||
[System.Boolean] | ||
$Recurse = $false | ||
|
||
[DscProperty()] | ||
[System.Boolean] | ||
$Force = $false | ||
|
||
[DscProperty()] | ||
[LinkBehavior] | ||
$Links = [LinkBehavior]::Follow | ||
|
||
[DscProperty()] | ||
[System.String] | ||
$Group | ||
|
||
[DscProperty()] | ||
[System.String] | ||
$Mode | ||
|
||
[DscProperty()] | ||
[System.String] | ||
$Owner | ||
|
||
[DscProperty(NotConfigurable)] | ||
[System.DateTime] | ||
$CreatedDate | ||
|
||
[DscProperty(NotConfigurable)] | ||
[System.DateTime] | ||
$ModifiedDate | ||
|
||
[DscProperty()] | ||
[Encoding] | ||
$Encoding = 'Default' | ||
|
||
[DscProperty()] | ||
[System.Boolean] | ||
$IgnoreTrailingWhitespace | ||
|
||
[DscProperty(NotConfigurable)] | ||
[FileSystemDscReason[]] | ||
$Reasons | ||
|
||
[FileSystemObject] Get () | ||
{ | ||
$returnable = @{ | ||
DestinationPath = $this.DestinationPath | ||
SourcePath = $this.SourcePath | ||
Ensure = $this.Ensure | ||
Type = $this.Type | ||
Contents = '' | ||
Checksum = $this.Checksum | ||
Recurse = $this.Recurse | ||
Force = $this.Force | ||
Links = $this.Links | ||
Encoding = $this.Encoding | ||
Group = '' | ||
Mode = '' | ||
Owner = '' | ||
IgnoreTrailingWhitespace = $this.IgnoreTrailingWhitespace | ||
CreatedDate = [datetime]::new(0) | ||
ModifiedDate = [datetime]::new(0) | ||
Reasons = @() | ||
} | ||
|
||
if ($this.Type -eq [objectType]::directory -and -not [string]::IsNullOrWhiteSpace($this.Contents)) | ||
{ | ||
Write-Verbose -Message "Type is directory, yet parameter Contents was used." | ||
$returnable.Reasons += @{ | ||
Code = "File:File:ParameterMismatch" | ||
Phrase = "Type is directory, yet parameter Contents was used." | ||
} | ||
return [FileSystemObject]$returnable | ||
} | ||
|
||
$object = Get-Item -ErrorAction SilentlyContinue -Path $this.DestinationPath -Force | ||
if ($null -eq $object -and $this.Ensure -eq [ensure]::present) | ||
{ | ||
Write-Verbose -Message "Object $($this.DestinationPath) does not exist, but Ensure is set to 'Present'" | ||
$returnable.Reasons += @{ | ||
Code = "File:File:ObjectMissingWhenItShouldExist" | ||
Phrase = "Object $($this.DestinationPath) does not exist, but Ensure is set to 'Present'" | ||
} | ||
return [FileSystemObject]$returnable | ||
} | ||
|
||
if ($null -ne $object -and $this.Ensure -eq [ensure]::absent) | ||
{ | ||
Write-Verbose -Message "Object $($this.DestinationPath) exists, but Ensure is set to 'Absent'" | ||
$returnable.Reasons += @{ | ||
Code = "File:File:ObjectExistsWhenItShouldNot" | ||
Phrase = "Object $($this.DestinationPath) exists, but Ensure is set to 'Absent'" | ||
} | ||
return [FileSystemObject]$returnable | ||
} | ||
|
||
if ($object.Count -eq 1 -and ($object.Attributes -band 'ReparsePoint') -eq 'ReparsePoint') | ||
{ | ||
$returnable.Type = 'SymbolicLink' | ||
} | ||
elseif ($object.Count -eq 1 -and ($object.Attributes -band 'Directory') -eq 'Directory') | ||
{ | ||
$returnable.Type = 'Directory' | ||
} | ||
elseif ($object.Count -eq 1) | ||
{ | ||
$returnable.Type = 'File' | ||
} | ||
|
||
if ($returnable.Type -ne $this.Type) | ||
{ | ||
$returnable.Reasons += @{ | ||
Code = "File:File:TypeMismatch" | ||
Phrase = "Type of $($object.FullName) has type '$($returnable.Type)', should be '$($this.Type)'" | ||
} | ||
} | ||
|
||
$returnable.DestinationPath = $object.FullName | ||
if ([string]::IsNullOrWhiteSpace($this.SourcePath) -and $object -and $this.Type -eq [objectType]::file) | ||
{ | ||
$returnable.Contents = Get-Content -Raw -Path $object.FullName -Encoding $this.Encoding.ToString() | ||
} | ||
|
||
if (-not $this.Ensure -eq 'Absent' -and -not [string]::IsNullOrWhiteSpace($returnable.Contents) -and $this.IgnoreTrailingWhitespace) | ||
{ | ||
$returnable.Contents = $returnable.Contents.Trim() | ||
} | ||
|
||
if (-not [string]::IsNullOrWhiteSpace($this.Contents) -and $returnable.Contents -ne $this.Contents) | ||
{ | ||
$returnable.Reasons += @{ | ||
Code = "File:File:ContentMismatch" | ||
Phrase = "Content of $($object.FullName) different from parameter Contents" | ||
} | ||
} | ||
|
||
if ($object.Count -eq 1) | ||
{ | ||
$returnable.CreatedDate = $object.CreationTime | ||
$returnable.ModifiedDate = $object.LastWriteTime | ||
$returnable.Owner = $object.User | ||
$returnable.Mode = $object.Mode | ||
$returnable.Group = $object.Group | ||
} | ||
|
||
if (-not [string]::IsNullOrWhiteSpace($this.SourcePath)) | ||
{ | ||
if (-not $this.Recurse -and $this.Type -eq [objectType]::directory) | ||
{ | ||
Write-Verbose -Message "Directory is copied without Recurse parameter. Skipping file checksum" | ||
return [FileSystemObject]$returnable | ||
} | ||
|
||
$destination = if (-not $this.Recurse -and $this.SourcePath -notmatch '\*\?\[\]') | ||
{ | ||
Join-Path $this.DestinationPath (Split-Path $this.SourcePath -Leaf) | ||
} | ||
else | ||
{ | ||
$this.DestinationPath | ||
} | ||
|
||
$currHash = $this.CompareHash($destination, $this.SourcePath, $this.Checksum, $this.Recurse) | ||
|
||
if ($currHash.Count -gt 0) | ||
{ | ||
Write-Verbose -Message "Hashes of files in $($this.DestinationPath) (comparison path used: $destination) different from hashes in $($this.SourcePath)" | ||
$returnable.Reasons += @{ | ||
Code = "File:File:HashMismatch" | ||
Phrase = "Hashes of files in $($this.DestinationPath) different from hashes in $($this.SourcePath)" | ||
} | ||
} | ||
} | ||
return [FileSystemObject]$returnable | ||
} | ||
|
||
[void] Set() | ||
{ | ||
if ($this.Ensure -eq 'Absent') | ||
{ | ||
Write-Verbose -Message "Removing $($this.DestinationPath) with Recurse and Force" | ||
Remove-Item -Recurse -Force -Path $this.DestinationPath | ||
return | ||
} | ||
|
||
if ($this.Type -in [objectType]::file, [objectType]::directory -and [string]::IsNullOrWhiteSpace($this.SourcePath)) | ||
{ | ||
Write-Verbose -Message "Creating new $($this.Type) $($this.DestinationPath), Force" | ||
$param = @{ | ||
ItemType = $this.Type | ||
Path = $this.DestinationPath | ||
} | ||
if ($this.Force) | ||
{ | ||
$param['Force'] = $true | ||
} | ||
$null = New-Item @param | ||
} | ||
|
||
if ($this.Type -eq [objectType]::SymbolicLink) | ||
{ | ||
Write-Verbose -Message "Creating new symbolic link $($this.DestinationPath) --> $($this.SourcePath)" | ||
New-Item -ItemType SymbolicLink -Path $this.DestinationPath -Value $this.SourcePath | ||
return | ||
} | ||
|
||
if ($this.Contents) | ||
{ | ||
Write-Verbose -Message "Setting content of $($this.DestinationPath) using $($this.Encoding)" | ||
$this.Contents | Set-Content -Path $this.DestinationPath -Force -Encoding $this.Encoding.ToString() -NoNewline | ||
} | ||
|
||
if ($this.SourcePath -and ($this.SourcePath -match '\*|\?\[\]') -and -not (Test-Path -Path $this.DestinationPath)) | ||
{ | ||
Write-Verbose -Message "Creating destination directory for wildcard copy $($this.DestinationPath)" | ||
$null = New-Item -ItemType Directory -Path $this.DestinationPath | ||
} | ||
|
||
if ($this.SourcePath) | ||
{ | ||
Write-Verbose -Message "Copying from $($this.SourcePath) to $($This.DestinationPath), Recurse is $($this.Recurse), Using the Force: $($this.Force)" | ||
$copyParam = @{ | ||
Path = $this.SourcePath | ||
Destination = $this.DestinationPath | ||
} | ||
if ($this.Recurse) | ||
{ | ||
$copyParam['Recurse'] = $this.Recurse | ||
} | ||
if ($this.Force) | ||
{ | ||
$copyParam['Force'] = $this.Force | ||
} | ||
Copy-Item @copyParam | ||
} | ||
} | ||
|
||
[bool] Test() | ||
{ | ||
$currentState = $this.Get() | ||
|
||
return ($currentState.Reasons.Count -eq 0) | ||
} | ||
|
||
[System.IO.FileInfo[]] CompareHash([string]$Path, [string]$ReferencePath, [checksumType]$Type = 'md5', [bool]$Recurse) | ||
{ | ||
[object[]]$sourceHashes = $this.GetHash($ReferencePath, $Type, $Recurse) | ||
[object[]]$hashes = $this.GetHash($Path, $Type, $Recurse) | ||
|
||
if ($hashes.Count -eq 0) | ||
{ | ||
return [System.IO.FileInfo[]]$sourceHashes.Path | ||
} | ||
|
||
$comparison = Compare-Object -ReferenceObject $sourceHashes -DifferenceObject $hashes -Property Hash -PassThru | Where-Object SideIndicator -eq '<=' | ||
return [System.IO.FileInfo[]]$comparison.Path | ||
} | ||
|
||
# Return type unclear and either Microsoft.PowerShell.Commands.FileHashInfo or PSCustomObject | ||
# Might be better to create a custom class for this | ||
[object[]] GetHash([string]$Path, [checksumType]$Type, [bool]$Recurse) | ||
{ | ||
$hashStrings = if ($Type -eq 'md5') | ||
{ | ||
Get-ChildItem -Recurse:$Recurse -Path $Path -Force -File | Get-FileHash -Algorithm md5 | ||
} | ||
else | ||
{ | ||
$propz = @( | ||
@{ | ||
Name = 'Path' | ||
Expression = { $_.FullName } | ||
} | ||
@{ | ||
Name = 'Algorithm' | ||
Expression = { $Type } | ||
} | ||
@{ | ||
Name = 'Hash' | ||
Expression = { if ($Type -eq 'CreationTime') | ||
{ | ||
$_.CreationTime | ||
} | ||
else | ||
{ | ||
$_.LastWriteTime | ||
} | ||
} | ||
} | ||
) | ||
Get-ChildItem -Recurse:$Recurse -Path $Path -Force -File | Select-Object -Property $propz | ||
} | ||
|
||
return $hashStrings | ||
} | ||
} |
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,6 @@ | ||
enum ChecksumType | ||
{ | ||
MD5 | ||
LastModifiedTime | ||
CreationTime | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resource name is
FileSystemObject
instead ofFile
.