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
23 changes: 23 additions & 0 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ $filesForWindowsPackage = @(
$filesForLinuxPackage = @(
'dsc',
'assertion.dsc.resource.json',
'apt.dsc.resource.json',
'apt.dsc.resource.sh',
'group.dsc.resource.json',
'powershell.dsc.resource.json',
'psDscAdapter/',
Expand All @@ -64,6 +66,12 @@ $filesForMacPackage = @(
'runcommandonset'
)

# the list of files other than the binaries which need to be executable
$filesToBeExecutable = @(
'apt.dsc.resource.sh',
'brew.dsc.resource.sh'
)

function Find-LinkExe {
try {
# this helper may not be needed anymore, but keeping in case the install doesn't work for everyone
Expand Down Expand Up @@ -162,6 +170,7 @@ if (!$SkipBuild) {
$windows_projects = @("pal", "registry", "reboot_pending", "wmi-adapter")

$macOS_projects = @("resources/brew")
$linux_projects = @("resources/apt")

# projects are in dependency order
$projects = @(
Expand Down Expand Up @@ -192,6 +201,10 @@ if (!$SkipBuild) {
$projects += $macOS_projects
}

if ($IsLinux) {
$projects += $linux_projects
}

$failed = $false
foreach ($project in $projects) {
## Build format_json
Expand Down Expand Up @@ -255,6 +268,16 @@ if (!$SkipBuild) {

Copy-Item "*.dsc.resource.json" $target -Force -ErrorAction Ignore

# be sure that the files that should be executable are executable
if ($IsLinux -or $IsMacOS) {
foreach ($exeFile in $filesToBeExecutable) {
$exePath = "$target/$exeFile"
if (test-path $exePath) {
chmod +x $exePath
}
}
}

} finally {
Pop-Location
}
Expand Down
7 changes: 6 additions & 1 deletion dsc/tests/dsc_resource_list.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ Describe 'Tests for listing resources' {
}

It 'dsc resource list --tags "<tags>" and --description "<description> work' -TestCases @(
@{ tags = 'linux'; description = $null; expectedCount = 1; expectedType = 'Microsoft/OSInfo' }
if ($IsLinux) {
@{ tags = 'linux'; description = $null; expectedCount = 2; expectedType = @('DSC.PackageManagement/Apt', 'Microsoft/OSInfo') }
}
else {
@{ tags = 'linux'; description = $null; expectedCount = 1; expectedType = 'Microsoft/OSInfo' }
}
@{ tags = $null; description = 'operating system'; expectedCount = 1; expectedType = 'Microsoft/OSInfo' }
@{ tags = 'linux'; description = 'operating system'; expectedCount = 1; expectedType = 'Microsoft/OSInfo' }
@{ tags = 'notfound'; description = 'operating system'; expectedCount = 0; expectedType = $null }
Expand Down
74 changes: 74 additions & 0 deletions resources/apt/apt.dsc.resource.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/resource/manifest.json",
"type": "DSC.PackageManagement/Apt",
"description": "Manage packages with the advanced package tool (APT)",
"tags": [
"Linux",
"apt",
"PackageManagement"
],
"version": "0.1.0",
"get": {
"executable": "apt.dsc.resource.sh",
"args": [
"get"
],
"input": "env"
},
"set": {
"executable": "apt.dsc.resource.sh",
"args": [
"set"
],
"input": "env",
"implementsPretest": true,
"handlesExist": true
},
"export": {
"executable": "apt.dsc.resource.sh",
"args": [
"export"
],
"input": "env"
},
"exitCodes": {
"0": "Success",
"1": "Invalid parameter"
},
"schema": {
"embedded": {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/resources/DSC/PackageManagement/apt/v0.1.0/schema.json",
"title": "Apt",
"description": "Managed packages using apt",
"type": "object",
"required": [
"packageName"
],
"additionalProperties": false,
"properties": {
"packageName": {
"type": "string",
"title": "Package Name",
"description": "Defines the name of the package to query or install"
},
"version": {
"type": "string",
"title": "Version",
"description": "Defines the version of the package to install"
},
"source": {
"type": "string",
"title": "Source",
"description": "Indicates the source of the package",
"readOnly": true
},
"_exist": {
"type": "boolean",
"title": "Exist",
"description": "Defines if the package should exist or not"
}
}
}
}
}
61 changes: 61 additions & 0 deletions resources/apt/apt.dsc.resource.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/bin/bash

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

export exist=true
export NONINTERACTIVE=1

# $packageName and $_exist are sent as env vars by dsc converting the JSON input to name/value pairs

check_args() {
if [[ -z $packageName ]]; then
echo "packageName not set"
exit 1
fi
}

get_apt() {
pkgname=$1
InstalledSection=0
apt list --installed $pkgname 2>&1 | while read line; do
if [[ $line == Listing* ]]; then
InstalledSection=1
elif [[ $InstalledSection = 1 ]]; then
echo $line | awk '{
split($0, a, " ");
split(a[1], pn, "/");
printf("{ \"_exist\": \"%s\", \"packageName\": \"%s\", \"version\": \"%s\", \"source\": \"%s\" }\n", ENVIRON["exist"], pn[1], a[2], pn[2]);
}'
fi
done
}

if [[ "$#" -eq "0" ]]; then
echo "Command not provided, valid commands: get, set, export"
exit 1
elif [[ "$1" == "get" ]]; then
check_args
output="$(get_apt $packageName)"
if [[ -z $output ]]; then
printf '{"_exist":"false","packageName":"%s","version":"","source":""}\n' $packageName
else
echo $output
fi
elif [[ "$1" == "set" ]]; then
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need to check if the resource is invoked as root? If I recall correctly, install and remove fail as normal users.

Copy link
Member

Choose a reason for hiding this comment

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

@michaeltlombardi perhaps we should have a general issue opened to add in the resource manifest when specific operations require elevated and DSC can fail fast

check_args
if [[ -z $_exist ]]; then
# if $_exist is not defined in the input, it defaults to `true`
_exist=true
fi
if [[ $_exist = true ]]; then
apt install -y "${packageName}"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this handle installing a specific version, since we have the version property? Or should version be marked as readOnly?

Copy link
Member

Choose a reason for hiding this comment

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

I think this resource should support specifying the version, but limit it to apt syntax

Copy link
Member

Choose a reason for hiding this comment

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

We can add this later

else
apt remove -y "${packageName}"
fi
elif [[ "$1" == "export" ]]; then
get_apt
else
echo "Invalid command, valid commands: get, set, export"
exit 1
fi
1 change: 1 addition & 0 deletions resources/apt/copy_files.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
apt.dsc.resource.sh
80 changes: 80 additions & 0 deletions resources/apt/test/apt.tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

Describe 'Apt resource tests' {
BeforeAll {
$aptExists = ($null -ne (Get-Command apt -CommandType Application -ErrorAction Ignore))
}

Context "export" {
It "should have more than 20 resources" -Skip:$(! $IsLinux) {
if (-not $aptExists) {
Set-ItResult -Skip -Because "Apt not found"
}

$result = dsc resource export --resource DSC.PackageManagement/Apt | ConvertFrom-Json
$result.resources.Count | Should -BeGreaterThan 20
}
}

Context "wget tests" {
BeforeAll {
$pkgName = "wget"
$yamlPath = "$PSScriptRoot/assets/apt_${pkgName}.dsc.yaml"
}

It 'Config get works' -Skip:$(! $IsLinux) {
if (-not $aptExists) {
Set-ItResult -Skip -Because "Apt not found"
}
$out = dsc config get -p $yamlPath | ConvertFrom-Json -Depth 10
$LASTEXITCODE | Should -Be 0
$exists = $null -ne (Get-Command $pkgName -CommandType Application -ErrorAction Ignore)
$observed = $out.results[1].result.actualState._exist
$observed | Should -Be $exists
}

It 'Config test works' -Skip:$(! $IsLinux) {
if (-not $aptExists) {
Set-ItResult -Skip -Because "Apt not found"
}

$out = dsc config test -p $yamlPath| ConvertFrom-Json -Depth 10
$LASTEXITCODE | Should -Be 0
$exists = $null -ne (Get-Command pkgName -CommandType Application -ErrorAction Ignore)
$out.results[1].result.inDesiredState | Should -Be $exists
}
}

Context "install/uninstall rolldice tests" {
BeforeAll {
$pkgName = "rolldice"
$yamlInstallPath = "$PSScriptRoot/assets/apt_install_${pkgName}.dsc.yaml"
$yamlUnInstallPath = "$PSScriptRoot/assets/apt_uninstall_${pkgName}.dsc.yaml"
}

It 'Can install a package' -Skip:$(! $IsLinux) {
Set-ItResult -Skip -Because "Apt requires sudo"

if (apt list $pkgname 2>&1 | Select-String installed ) {
apt remove -y $pkgname
}

$result = dsc config set -p $yamlInstallPath | ConvertFrom-Json
$result.results[1].result.beforestate._exist | Should -Be false
$result.results[1].result.afterstate._exist | Should -Be true
}

It 'Can uninstall a package' -Skip:$(! $IsLinux) {
Set-ItResult -Skip -Because "Apt requires sudo"

if ($null -eq (apt list $pkgName 2>&1 | Select-String installed)) {
apt install -y $pkgname
}

$result = dsc config set -p $yamlUnInstallPath | ConvertFrom-Json
$result.results[1].result.beforestate._exist | Should -Be true
$result.results[1].result.afterstate._exist | Should -Be false
}
}
}
19 changes: 19 additions & 0 deletions resources/apt/test/assets/apt_install_rolldice.dsc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Example to see if PowerShell 7 is installed, install it, or get all installed packages
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: assertions
type: Microsoft.DSC/Assertion
properties:
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: os_check
type: Microsoft/OSInfo
properties:
family: Linux
- name: apt_rolldice
type: DSC.PackageManagement/Apt
properties:
packageName: rolldice
_exist: true
Copy link
Member

Choose a reason for hiding this comment

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

It would be better if this value was defined as a parameter so instead of having two similar config, you could just pass in false (and default to true). See https://github.com/PowerShell/DSC/blob/main/dsc/tests/dsc_parameters.tests.ps1 for examples

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm happy to leave that as an exercise for the reader

dependsOn:
- "[resourceId('Microsoft.DSC/Assertion','assertions')]"
19 changes: 19 additions & 0 deletions resources/apt/test/assets/apt_uninstall_rolldice.dsc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Example to see if PowerShell 7 is installed, install it, or get all installed packages
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: assertions
type: Microsoft.DSC/Assertion
properties:
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: os_check
type: Microsoft/OSInfo
properties:
family: Linux
- name: apt_rolldice
type: DSC.PackageManagement/Apt
properties:
packageName: rolldice
_exist: false
dependsOn:
- "[resourceId('Microsoft.DSC/Assertion','assertions')]"
19 changes: 19 additions & 0 deletions resources/apt/test/assets/apt_wget.dsc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Example to see if PowerShell 7 is installed, install it, or get all installed packages
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: assertions
type: Microsoft.DSC/Assertion
properties:
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: os_check
type: Microsoft/OSInfo
properties:
family: Linux
- name: apt_wget
type: DSC.PackageManagement/Apt
properties:
packageName: wget
Copy link
Member

Choose a reason for hiding this comment

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

packageName should probably also be a parameter

_exist: true
dependsOn:
- "[resourceId('Microsoft.DSC/Assertion','assertions')]"