Skip to content

Commit 06e79cd

Browse files
authoredMay 16, 2024··
Merge pull request #434 from JamesWTruher/apt001
Create apt package management resource.
2 parents 1572460 + e28061b commit 06e79cd

9 files changed

+302
-1
lines changed
 

‎build.ps1

+23
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ $filesForWindowsPackage = @(
4545
$filesForLinuxPackage = @(
4646
'dsc',
4747
'assertion.dsc.resource.json',
48+
'apt.dsc.resource.json',
49+
'apt.dsc.resource.sh',
4850
'group.dsc.resource.json',
4951
'powershell.dsc.resource.json',
5052
'psDscAdapter/',
@@ -64,6 +66,12 @@ $filesForMacPackage = @(
6466
'runcommandonset'
6567
)
6668

69+
# the list of files other than the binaries which need to be executable
70+
$filesToBeExecutable = @(
71+
'apt.dsc.resource.sh',
72+
'brew.dsc.resource.sh'
73+
)
74+
6775
function Find-LinkExe {
6876
try {
6977
# this helper may not be needed anymore, but keeping in case the install doesn't work for everyone
@@ -162,6 +170,7 @@ if (!$SkipBuild) {
162170
$windows_projects = @("pal", "registry", "reboot_pending", "wmi-adapter")
163171

164172
$macOS_projects = @("resources/brew")
173+
$linux_projects = @("resources/apt")
165174

166175
# projects are in dependency order
167176
$projects = @(
@@ -192,6 +201,10 @@ if (!$SkipBuild) {
192201
$projects += $macOS_projects
193202
}
194203

204+
if ($IsLinux) {
205+
$projects += $linux_projects
206+
}
207+
195208
$failed = $false
196209
foreach ($project in $projects) {
197210
## Build format_json
@@ -255,6 +268,16 @@ if (!$SkipBuild) {
255268

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

271+
# be sure that the files that should be executable are executable
272+
if ($IsLinux -or $IsMacOS) {
273+
foreach ($exeFile in $filesToBeExecutable) {
274+
$exePath = "$target/$exeFile"
275+
if (test-path $exePath) {
276+
chmod +x $exePath
277+
}
278+
}
279+
}
280+
258281
} finally {
259282
Pop-Location
260283
}

‎dsc/tests/dsc_resource_list.tests.ps1

+6-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ Describe 'Tests for listing resources' {
1717
}
1818

1919
It 'dsc resource list --tags "<tags>" and --description "<description> work' -TestCases @(
20-
@{ tags = 'linux'; description = $null; expectedCount = 1; expectedType = 'Microsoft/OSInfo' }
20+
if ($IsLinux) {
21+
@{ tags = 'linux'; description = $null; expectedCount = 2; expectedType = @('DSC.PackageManagement/Apt', 'Microsoft/OSInfo') }
22+
}
23+
else {
24+
@{ tags = 'linux'; description = $null; expectedCount = 1; expectedType = 'Microsoft/OSInfo' }
25+
}
2126
@{ tags = $null; description = 'operating system'; expectedCount = 1; expectedType = 'Microsoft/OSInfo' }
2227
@{ tags = 'linux'; description = 'operating system'; expectedCount = 1; expectedType = 'Microsoft/OSInfo' }
2328
@{ tags = 'notfound'; description = 'operating system'; expectedCount = 0; expectedType = $null }

‎resources/apt/apt.dsc.resource.json

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/resource/manifest.json",
3+
"type": "DSC.PackageManagement/Apt",
4+
"description": "Manage packages with the advanced package tool (APT)",
5+
"tags": [
6+
"Linux",
7+
"apt",
8+
"PackageManagement"
9+
],
10+
"version": "0.1.0",
11+
"get": {
12+
"executable": "apt.dsc.resource.sh",
13+
"args": [
14+
"get"
15+
],
16+
"input": "env"
17+
},
18+
"set": {
19+
"executable": "apt.dsc.resource.sh",
20+
"args": [
21+
"set"
22+
],
23+
"input": "env",
24+
"implementsPretest": true,
25+
"handlesExist": true
26+
},
27+
"export": {
28+
"executable": "apt.dsc.resource.sh",
29+
"args": [
30+
"export"
31+
],
32+
"input": "env"
33+
},
34+
"exitCodes": {
35+
"0": "Success",
36+
"1": "Invalid parameter"
37+
},
38+
"schema": {
39+
"embedded": {
40+
"$schema": "http://json-schema.org/draft-07/schema#",
41+
"$id": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/resources/DSC/PackageManagement/apt/v0.1.0/schema.json",
42+
"title": "Apt",
43+
"description": "Managed packages using apt",
44+
"type": "object",
45+
"required": [
46+
"packageName"
47+
],
48+
"additionalProperties": false,
49+
"properties": {
50+
"packageName": {
51+
"type": "string",
52+
"title": "Package Name",
53+
"description": "Defines the name of the package to query or install"
54+
},
55+
"version": {
56+
"type": "string",
57+
"title": "Version",
58+
"description": "Defines the version of the package to install"
59+
},
60+
"source": {
61+
"type": "string",
62+
"title": "Source",
63+
"description": "Indicates the source of the package",
64+
"readOnly": true
65+
},
66+
"_exist": {
67+
"type": "boolean",
68+
"title": "Exist",
69+
"description": "Defines if the package should exist or not"
70+
}
71+
}
72+
}
73+
}
74+
}

‎resources/apt/apt.dsc.resource.sh

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/bin/bash
2+
3+
# Copyright (c) Microsoft Corporation.
4+
# Licensed under the MIT License.
5+
6+
export exist=true
7+
export NONINTERACTIVE=1
8+
9+
# $packageName and $_exist are sent as env vars by dsc converting the JSON input to name/value pairs
10+
11+
check_args() {
12+
if [[ -z $packageName ]]; then
13+
echo "packageName not set"
14+
exit 1
15+
fi
16+
}
17+
18+
get_apt() {
19+
pkgname=$1
20+
InstalledSection=0
21+
apt list --installed $pkgname 2>&1 | while read line; do
22+
if [[ $line == Listing* ]]; then
23+
InstalledSection=1
24+
elif [[ $InstalledSection = 1 ]]; then
25+
echo $line | awk '{
26+
split($0, a, " ");
27+
split(a[1], pn, "/");
28+
printf("{ \"_exist\": \"%s\", \"packageName\": \"%s\", \"version\": \"%s\", \"source\": \"%s\" }\n", ENVIRON["exist"], pn[1], a[2], pn[2]);
29+
}'
30+
fi
31+
done
32+
}
33+
34+
if [[ "$#" -eq "0" ]]; then
35+
echo "Command not provided, valid commands: get, set, export"
36+
exit 1
37+
elif [[ "$1" == "get" ]]; then
38+
check_args
39+
output="$(get_apt $packageName)"
40+
if [[ -z $output ]]; then
41+
printf '{"_exist":"false","packageName":"%s","version":"","source":""}\n' $packageName
42+
else
43+
echo $output
44+
fi
45+
elif [[ "$1" == "set" ]]; then
46+
check_args
47+
if [[ -z $_exist ]]; then
48+
# if $_exist is not defined in the input, it defaults to `true`
49+
_exist=true
50+
fi
51+
if [[ $_exist = true ]]; then
52+
apt install -y "${packageName}"
53+
else
54+
apt remove -y "${packageName}"
55+
fi
56+
elif [[ "$1" == "export" ]]; then
57+
get_apt
58+
else
59+
echo "Invalid command, valid commands: get, set, export"
60+
exit 1
61+
fi

‎resources/apt/copy_files.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
apt.dsc.resource.sh

‎resources/apt/test/apt.tests.ps1

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
Describe 'Apt resource tests' {
5+
BeforeAll {
6+
$aptExists = ($null -ne (Get-Command apt -CommandType Application -ErrorAction Ignore))
7+
}
8+
9+
Context "export" {
10+
It "should have more than 20 resources" -Skip:$(! $IsLinux) {
11+
if (-not $aptExists) {
12+
Set-ItResult -Skip -Because "Apt not found"
13+
}
14+
15+
$result = dsc resource export --resource DSC.PackageManagement/Apt | ConvertFrom-Json
16+
$result.resources.Count | Should -BeGreaterThan 20
17+
}
18+
}
19+
20+
Context "wget tests" {
21+
BeforeAll {
22+
$pkgName = "wget"
23+
$yamlPath = "$PSScriptRoot/assets/apt_${pkgName}.dsc.yaml"
24+
}
25+
26+
It 'Config get works' -Skip:$(! $IsLinux) {
27+
if (-not $aptExists) {
28+
Set-ItResult -Skip -Because "Apt not found"
29+
}
30+
$out = dsc config get -p $yamlPath | ConvertFrom-Json -Depth 10
31+
$LASTEXITCODE | Should -Be 0
32+
$exists = $null -ne (Get-Command $pkgName -CommandType Application -ErrorAction Ignore)
33+
$observed = $out.results[1].result.actualState._exist
34+
$observed | Should -Be $exists
35+
}
36+
37+
It 'Config test works' -Skip:$(! $IsLinux) {
38+
if (-not $aptExists) {
39+
Set-ItResult -Skip -Because "Apt not found"
40+
}
41+
42+
$out = dsc config test -p $yamlPath| ConvertFrom-Json -Depth 10
43+
$LASTEXITCODE | Should -Be 0
44+
$exists = $null -ne (Get-Command pkgName -CommandType Application -ErrorAction Ignore)
45+
$out.results[1].result.inDesiredState | Should -Be $exists
46+
}
47+
}
48+
49+
Context "install/uninstall rolldice tests" {
50+
BeforeAll {
51+
$pkgName = "rolldice"
52+
$yamlInstallPath = "$PSScriptRoot/assets/apt_install_${pkgName}.dsc.yaml"
53+
$yamlUnInstallPath = "$PSScriptRoot/assets/apt_uninstall_${pkgName}.dsc.yaml"
54+
}
55+
56+
It 'Can install a package' -Skip:$(! $IsLinux) {
57+
Set-ItResult -Skip -Because "Apt requires sudo"
58+
59+
if (apt list $pkgname 2>&1 | Select-String installed ) {
60+
apt remove -y $pkgname
61+
}
62+
63+
$result = dsc config set -p $yamlInstallPath | ConvertFrom-Json
64+
$result.results[1].result.beforestate._exist | Should -Be false
65+
$result.results[1].result.afterstate._exist | Should -Be true
66+
}
67+
68+
It 'Can uninstall a package' -Skip:$(! $IsLinux) {
69+
Set-ItResult -Skip -Because "Apt requires sudo"
70+
71+
if ($null -eq (apt list $pkgName 2>&1 | Select-String installed)) {
72+
apt install -y $pkgname
73+
}
74+
75+
$result = dsc config set -p $yamlUnInstallPath | ConvertFrom-Json
76+
$result.results[1].result.beforestate._exist | Should -Be true
77+
$result.results[1].result.afterstate._exist | Should -Be false
78+
}
79+
}
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Example to see if PowerShell 7 is installed, install it, or get all installed packages
2+
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
3+
resources:
4+
- name: assertions
5+
type: Microsoft.DSC/Assertion
6+
properties:
7+
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
8+
resources:
9+
- name: os_check
10+
type: Microsoft/OSInfo
11+
properties:
12+
family: Linux
13+
- name: apt_rolldice
14+
type: DSC.PackageManagement/Apt
15+
properties:
16+
packageName: rolldice
17+
_exist: true
18+
dependsOn:
19+
- "[resourceId('Microsoft.DSC/Assertion','assertions')]"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Example to see if PowerShell 7 is installed, install it, or get all installed packages
2+
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
3+
resources:
4+
- name: assertions
5+
type: Microsoft.DSC/Assertion
6+
properties:
7+
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
8+
resources:
9+
- name: os_check
10+
type: Microsoft/OSInfo
11+
properties:
12+
family: Linux
13+
- name: apt_rolldice
14+
type: DSC.PackageManagement/Apt
15+
properties:
16+
packageName: rolldice
17+
_exist: false
18+
dependsOn:
19+
- "[resourceId('Microsoft.DSC/Assertion','assertions')]"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Example to see if PowerShell 7 is installed, install it, or get all installed packages
2+
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
3+
resources:
4+
- name: assertions
5+
type: Microsoft.DSC/Assertion
6+
properties:
7+
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
8+
resources:
9+
- name: os_check
10+
type: Microsoft/OSInfo
11+
properties:
12+
family: Linux
13+
- name: apt_wget
14+
type: DSC.PackageManagement/Apt
15+
properties:
16+
packageName: wget
17+
_exist: true
18+
dependsOn:
19+
- "[resourceId('Microsoft.DSC/Assertion','assertions')]"

0 commit comments

Comments
 (0)
Please sign in to comment.