Compare commits
11 Commits
ubuntu/1.0
...
ubuntu/plu
Author | SHA1 | Date | |
---|---|---|---|
31a969624e | |||
6e48a9bf83 | |||
0f9148c4e5 | |||
cfa44c39d5 | |||
85a6d759fd | |||
6dbafb28d0 | |||
815e7da0b9 | |||
bcb4235741 | |||
8169439498 | |||
ea0c831c65 | |||
0e4925cf0b |
1
debian/.gitignore
vendored
Normal file
1
debian/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
files
|
32
debian/changelog
vendored
32
debian/changelog
vendored
@ -1,3 +1,35 @@
|
||||
snapd-extra-utils (1.1.1) plucky; urgency=medium
|
||||
|
||||
* Split apart autopkgtests to prevent neutral on some arches.
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Fri, 21 Feb 2025 13:45:49 -0600
|
||||
|
||||
snapd-extra-utils (1.1.0) plucky; urgency=medium
|
||||
|
||||
* [snapd-seed-glue] Fix installation of snaps in Calamares Full Installation
|
||||
due to a missing account key, and add an autopkgtest to ensure we catch
|
||||
this ahead of time in the future (LP: #2096649).
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Thu, 20 Feb 2025 14:09:13 -0600
|
||||
|
||||
snapd-extra-utils (1.0.7) plucky; urgency=medium
|
||||
|
||||
* Add several extra Breaks/Replaces to be safe.
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Wed, 11 Dec 2024 22:18:42 -0600
|
||||
|
||||
snapd-extra-utils (1.0.6) plucky; urgency=medium
|
||||
|
||||
* [autopkgtest] Further refactoring to properly run the autopkgtest.
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Mon, 25 Nov 2024 01:57:44 -0600
|
||||
|
||||
snapd-extra-utils (1.0.5) plucky; urgency=medium
|
||||
|
||||
* [autopkgtest] Convert the snapd-seed-glue autopkgtest to C++.
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Sun, 24 Nov 2024 23:12:13 -0600
|
||||
|
||||
snapd-extra-utils (1.0.4) plucky; urgency=medium
|
||||
|
||||
* [autopkgtest] Use snaps that are available on all arches, to fix armhf,
|
||||
|
4
debian/control
vendored
4
debian/control
vendored
@ -17,6 +17,8 @@ Vcs-Git: https://git.lubuntu.me/Lubuntu/snapd-extra-utils.git
|
||||
Package: snapd-seed-glue
|
||||
Architecture: any
|
||||
Depends: snapd, xdelta3, ${misc:Depends}, ${shlibs:Depends}
|
||||
Breaks: calamares-settings-ubuntu-common (<< 1:25.04.1)
|
||||
Replaces: calamares-settings-ubuntu-common (<< 1:25.04.1)
|
||||
Description: Installer and pre-seed utilities for snapd
|
||||
Primarily used in Calamares, snapd-seed-glue updates snap seeds in a given
|
||||
directory, on a pre-booted system. It handles dependency resolution, delta
|
||||
@ -25,6 +27,8 @@ Description: Installer and pre-seed utilities for snapd
|
||||
Package: snapd-installation-monitor
|
||||
Architecture: any
|
||||
Depends: snapd, ${misc:Depends}, ${shlibs:Depends}
|
||||
Breaks: lubuntu-snap-installation-monitor
|
||||
Replaces: lubuntu-snap-installation-monitor
|
||||
Description: First-boot snap install notification
|
||||
When many snaps are preseeded, on first boot the user may be confused if they
|
||||
can not open one of those snaps. This simple notification informs the user,
|
||||
|
4
debian/rules
vendored
4
debian/rules
vendored
@ -3,10 +3,12 @@ export DH_VERBOSE = 1
|
||||
export GO111MODULE = off
|
||||
export GOPATH=/usr/share/gocode
|
||||
export GOCACHE=$(CURDIR)/.gocache
|
||||
export NUM_CPUS = $(shell nproc)
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
override_dh_auto_build:
|
||||
(cd snapd-seed-glue && go build -gcflags="all=-N -l" -ldflags="-compressdwarf=false" -o snapd-seed-glue)
|
||||
(cd snapd-installation-monitor && cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo . && make)
|
||||
(cd snapd-seed-glue/tests && cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo . && make -j${NUM_CPUS})
|
||||
(cd snapd-installation-monitor && cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo . && make -j${NUM_CPUS})
|
||||
|
12
debian/tests/control
vendored
12
debian/tests/control
vendored
@ -1,3 +1,9 @@
|
||||
Tests: snapd-seed-glue
|
||||
Depends: snapd-seed-glue
|
||||
Restrictions: needs-internet
|
||||
Test-Command: snapd-seed-glue/tests/snapd_seed_glue_test
|
||||
Depends: @builddeps@, snapd-seed-glue
|
||||
Restrictions: needs-internet, build-needed
|
||||
Architecture: !amd64
|
||||
|
||||
Test-Command: snapd-seed-glue/tests/snapd_seed_glue_test
|
||||
Depends: @builddeps@, snapd-seed-glue, livecd-rootfs, squashfs-tools
|
||||
Restrictions: needs-internet, build-needed, isolation-machine, needs-sudo
|
||||
Architecture: amd64
|
||||
|
40
debian/tests/snapd-seed-glue
vendored
40
debian/tests/snapd-seed-glue
vendored
@ -1,40 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# File to track command output, so we can check for successful completion
|
||||
OUTPUT_FILE=$(mktemp)
|
||||
|
||||
# If the command output does not contain a final confirmation of success, exit 1
|
||||
confirm_success () {
|
||||
if grep -q "Cleanup and validation completed" "$OUTPUT_FILE"; then
|
||||
rm -f "$OUTPUT_FILE"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to run snapd-seed-glue with given arguments
|
||||
run_snapd_seed_glue () {
|
||||
/usr/bin/snapd-seed-glue --verbose --seed hello_test "$@" > >(tee -a "$OUTPUT_FILE") 2>&1
|
||||
confirm_success
|
||||
}
|
||||
|
||||
echo "[snapd-seed-glue autopkgtest] Testing snapd-seed-glue with hello..."
|
||||
|
||||
run_snapd_seed_glue hello
|
||||
|
||||
echo "[snapd-seed-glue autopkgtest] Add htop to the same seed..."
|
||||
|
||||
run_snapd_seed_glue hello htop
|
||||
|
||||
echo "[snapd-seed-glue autopkgtest] Remove htop and replace it with btop..."
|
||||
|
||||
run_snapd_seed_glue hello btop
|
||||
|
||||
echo "[snapd-seed-glue autopkgtest] Confirm that non-existent snaps will fail..."
|
||||
|
||||
/usr/bin/snapd-seed-glue --verbose --seed test_dir absolutelyridiculouslongnamethatwilldefinitelyneverexist > >(tee -a "$OUTPUT_FILE") 2>&1 || echo "Fail expected"
|
||||
if ! grep -q "cannot install snap \"absolutelyridiculouslongnamethatwilldefinitelyneverexist\": snap not found" "$OUTPUT_FILE"; then
|
||||
exit 1
|
||||
fi
|
@ -73,25 +73,45 @@ func downloadAssertions(storeClient *store.Store, snapInfo *snap.Info, downloadD
|
||||
return fmt.Errorf("failed to fetch account-key assertion for snap %s: %w", snapInfo.SuggestedName, err)
|
||||
}
|
||||
|
||||
// Step 4: Fetch account assertion using publisher-id
|
||||
accountAssertion, err := storeClient.Assertion(assertionTypes["account"], []string{publisherID}, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch account assertion for snap %s: %w", snapInfo.SuggestedName, err)
|
||||
}
|
||||
|
||||
// Step 5: Fetch snap-revision assertion
|
||||
// Step 4: Fetch snap-revision assertion
|
||||
snapSHA384Bytes, err := hex.DecodeString(snapSHA)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding SHA3-384 hex string for snap %s: %w", snapInfo.SuggestedName, err)
|
||||
}
|
||||
snapSHA384Base64 := base64.RawURLEncoding.EncodeToString(snapSHA384Bytes)
|
||||
//revisionKey := fmt.Sprintf("%s/global-upload", snapSHA384Base64)
|
||||
revisionKey := fmt.Sprintf("%s/", snapSHA384Base64)
|
||||
|
||||
snapRevisionAssertion, err := storeClient.Assertion(assertionTypes["snap-revision"], []string{revisionKey}, nil)
|
||||
if err != nil {
|
||||
verboseLog("Failed to fetch snap-revision assertion for snap %s: %v", snapInfo.SuggestedName, err)
|
||||
// Proceeding without snap-revision might be acceptable based on your use-case
|
||||
}
|
||||
|
||||
// Step 5: Fetch account assertions
|
||||
publisherAccountAssertion, err := storeClient.Assertion(assertionTypes["account"], []string{publisherID}, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch developer account assertion for snap %s: %w", snapInfo.SuggestedName, err)
|
||||
}
|
||||
|
||||
// Step 5.1: Determine authority account from snap-declaration
|
||||
authorityID, ok := snapDecl.Header("authority-id").(string)
|
||||
if !ok || authorityID == "" {
|
||||
return fmt.Errorf("snap-declaration assertion missing 'authority-id' header for snap %s", snapInfo.SuggestedName)
|
||||
}
|
||||
|
||||
// Step 5.2: Fetch authority account assertion
|
||||
authorityAccountAssertion, err := storeClient.Assertion(assertionTypes["account"], []string{authorityID}, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch authority account assertion for snap %s: %w", snapInfo.SuggestedName, err)
|
||||
}
|
||||
|
||||
// Step 5.3: Fetch developer account assertion
|
||||
developerID, ok := snapRevisionAssertion.Header("developer-id").(string)
|
||||
developerAccountAssertion := authorityAccountAssertion
|
||||
if ok && authorityID != "" {
|
||||
developerAccountAssertion, err = storeClient.Assertion(assertionTypes["account"], []string{developerID}, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch developer account assertion for snap %s: %w", snapInfo.SuggestedName, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6: Write assertions in the desired order
|
||||
@ -99,12 +119,20 @@ func downloadAssertions(storeClient *store.Store, snapInfo *snap.Info, downloadD
|
||||
writeAssertion("account-key", accountKeyAssertion, assertionsFile)
|
||||
|
||||
// 2. account
|
||||
writeAssertion("account", accountAssertion, assertionsFile)
|
||||
writeAssertion("account", publisherAccountAssertion, assertionsFile)
|
||||
if authorityID != publisherID {
|
||||
writeAssertion("account", authorityAccountAssertion, assertionsFile)
|
||||
}
|
||||
|
||||
// 3. snap-declaration
|
||||
writeAssertion("snap-declaration", snapDecl, assertionsFile)
|
||||
|
||||
// 4. snap-revision (if fetched successfully)
|
||||
// 4. developer account if present
|
||||
if developerAccountAssertion != authorityAccountAssertion {
|
||||
writeAssertion("account", developerAccountAssertion, assertionsFile)
|
||||
}
|
||||
|
||||
// 5. snap-revision (if fetched successfully)
|
||||
if snapRevisionAssertion != nil {
|
||||
writeAssertion("snap-revision", snapRevisionAssertion, assertionsFile)
|
||||
}
|
||||
@ -138,11 +166,13 @@ func writeAssertion(assertionType string, assertion asserts.Assertion, file *os.
|
||||
body := assertion.Body()
|
||||
bodyLength := len(body)
|
||||
headers := assertion.Headers()
|
||||
verboseLog("Assertion headers: %v", headers)
|
||||
|
||||
// Only write the account assertion if it is not Canonical
|
||||
if assertionType == "account" {
|
||||
value, exists := headers["username"]
|
||||
if exists && value == "canonical" {
|
||||
verboseLog("Skipping assertion due to duplication in account file")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
7
snapd-seed-glue/tests/CMakeLists.txt
Normal file
7
snapd-seed-glue/tests/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(snapd_seed_glue_test)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
add_executable(snapd_seed_glue_test cli-tests.cpp)
|
125
snapd-seed-glue/tests/cli-tests.cpp
Normal file
125
snapd-seed-glue/tests/cli-tests.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
// Copyright (C) 2024-2025 Simon Quigley <tsimonq2@ubuntu.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#if defined(__x86_64__) || defined(__amd64__)
|
||||
#include <filesystem>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
std::string OUTPUT_FILE_CONTENTS;
|
||||
|
||||
std::pair<std::string, int> execute_command(const std::string& cmd) {
|
||||
std::array<char, 128> buffer{};
|
||||
std::string result;
|
||||
// Redirect stderr to stdout
|
||||
std::string cmd_with_redirect = cmd + " 2>&1";
|
||||
FILE* pipe = popen(cmd_with_redirect.c_str(), "r");
|
||||
if (!pipe) throw std::runtime_error("popen() failed!");
|
||||
while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) {
|
||||
result += buffer.data();
|
||||
// Also echo the output to stdout
|
||||
std::cout << buffer.data();
|
||||
}
|
||||
int rc = pclose(pipe);
|
||||
int exit_code = WEXITSTATUS(rc);
|
||||
return {result, exit_code};
|
||||
}
|
||||
|
||||
void confirm_success() {
|
||||
if (OUTPUT_FILE_CONTENTS.find("Cleanup and validation completed") != std::string::npos) OUTPUT_FILE_CONTENTS.clear();
|
||||
else exit(1);
|
||||
}
|
||||
|
||||
void run_snapd_seed_glue(const std::vector<std::string>& args, const std::string& dir = "") {
|
||||
std::string cmd;
|
||||
if (!dir.empty()) cmd = std::format("snapd-seed-glue/snapd-seed-glue --verbose --seed {}", dir);
|
||||
else cmd = "snapd-seed-glue/snapd-seed-glue --verbose --seed hello_test";
|
||||
for (const auto& arg : args) cmd += " " + arg;
|
||||
|
||||
auto [output, exit_code] = execute_command(cmd);
|
||||
OUTPUT_FILE_CONTENTS += output;
|
||||
if (exit_code != 0) exit(exit_code);
|
||||
confirm_success();
|
||||
}
|
||||
|
||||
#if defined(__x86_64__) || defined(__amd64__)
|
||||
std::string get_version_codename() {
|
||||
std::ifstream file("/etc/os-release");
|
||||
if (!file.is_open()) return {};
|
||||
std::string line;
|
||||
constexpr std::string_view key = "VERSION_CODENAME=";
|
||||
while (std::getline(file, line)) if (line.starts_with(key)) return line.substr(key.size());
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
std::cout << "[snapd-seed-glue autopkgtest] Testing snapd-seed-glue with hello...\n";
|
||||
run_snapd_seed_glue({"hello"});
|
||||
|
||||
std::cout << "[snapd-seed-glue autopkgtest] Add htop to the same seed...\n";
|
||||
run_snapd_seed_glue({"hello", "htop"});
|
||||
|
||||
std::cout << "[snapd-seed-glue autopkgtest] Remove htop and replace it with btop...\n";
|
||||
run_snapd_seed_glue({"hello", "btop"});
|
||||
|
||||
std::cout << "[snapd-seed-glue autopkgtest] Confirm that non-existent snaps will fail...\n";
|
||||
std::string invalid_snap = "absolutelyridiculouslongnamethatwilldefinitelyneverexist";
|
||||
std::string cmd = "/usr/bin/snapd-seed-glue --verbose --seed test_dir " + invalid_snap;
|
||||
auto [output, exit_code] = execute_command(cmd);
|
||||
OUTPUT_FILE_CONTENTS += output;
|
||||
if (exit_code != 0) std::cout << "Fail expected\n";
|
||||
if (OUTPUT_FILE_CONTENTS.find("cannot install snap \"" + invalid_snap + "\": snap not found") == std::string::npos) exit(1);
|
||||
|
||||
#if defined(__x86_64__) || defined(__amd64__)
|
||||
std::cout << "[snapd-seed-glue autopkgtest] Confirm that a livefs can be created, and snapd-seed-glue can be used...\n";
|
||||
// Logic taken from lp:launchpad-buildd/lpbuildd/target/build_livefs.py
|
||||
setenv("PROJECT", "lubuntu", 1);
|
||||
setenv("ARCH", "amd64", 1);
|
||||
setenv("SUITE", get_version_codename().c_str(), 1);
|
||||
|
||||
if (!std::filesystem::create_directory("auto")) exit(1);
|
||||
for (std::string lb_script : {"config", "build", "clean"}) {
|
||||
auto [link_output, link_exit_code] = execute_command(std::format("ln -s /usr/share/livecd-rootfs/live-build/auto/{} auto/", lb_script));
|
||||
if (link_exit_code != 0) exit(link_exit_code);
|
||||
}
|
||||
auto [clean_output, clean_exit_code] = execute_command("sudo -E lb clean --purge");
|
||||
if (clean_exit_code != 0) exit(clean_exit_code);
|
||||
auto [config_output, config_exit_code] = execute_command("lb config");
|
||||
if (config_exit_code != 0) exit(config_exit_code);
|
||||
auto [build_output, build_exit_code] = execute_command("sudo -E lb build");
|
||||
if (build_exit_code != 0) exit(build_exit_code);
|
||||
auto [unsquashfs_output, unsquashfs_exit_code] = execute_command("sudo unsquashfs livecd.lubuntu.minimal.standard.squashfs /var/lib/snapd/seed/");
|
||||
if (unsquashfs_exit_code != 0) exit(unsquashfs_exit_code);
|
||||
auto [clean2_output, clean2_exit_code] = execute_command("sudo -E lb clean --purge");
|
||||
if (clean2_exit_code != 0) exit(clean2_exit_code);
|
||||
auto [chown_output, chown_exit_code] = execute_command(std::format("sudo chown -R {}:{} squashfs-root/", getuid(), getgid()));
|
||||
if (chown_exit_code != 0) exit(chown_exit_code);
|
||||
|
||||
std::cout << "[snapd-seed-glue autopkgtest] A livefs can be created. Confirm snapd-seed-glue can be used...\n";
|
||||
run_snapd_seed_glue({"firefox", "firmware-updater"}, "squashfs-root/var/lib/snapd/seed/");
|
||||
run_snapd_seed_glue({"firefox", "firmware-updater", "krita", "thunderbird"}, "squashfs-root/var/lib/snapd/seed/");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user