Skip to content

Commit

Permalink
Merge pull request #1023 from yast/merge-sle-25560
Browse files Browse the repository at this point in the history
 SLE#25560: do not create a snapshot after install/upgrade if root filesystem is RO (master)
  • Loading branch information
imobachgs committed Feb 21, 2022
2 parents b53af93 + c3a128a commit 5b0df8d
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 11 deletions.
79 changes: 79 additions & 0 deletions doc/snapshots.md
@@ -0,0 +1,79 @@
# Installation and upgrade snapshots

This document tries to clarify how YaST, as an installer, creates snapshots during installation and
offline upgrade when Snapper is enabled. In a nutshell, YaST creates these snapshots:

* A *single* snapshot at the end of the installation (except for transactional systems, like
MicroOS).
* A *pre* snapshot at the beginning of the upgrade and a *post* snapshot at the end.

However, things are not that simple, so let's try to draw a full picture of this feature.

## Non-transactional systems

Let's start considering non transactional systems, like SUSE Linux Enterprise, openSUSE Leap or
openSUSE Tumbleweed. Remember that you can use openSUSE Tumbleweed as a transactional system if
you select the *Transactional Server* role during installation.

### Installation

YaST creates a snapshot after *finishing* the normal installation. If you are using AutoYaST, it
takes the snapshot at the end of the 2nd stage *unless it is disabled*. If the 2nd stage is
disabled, YaST takes the snapshot at the end of the 1st stage, just like a normal installation.

If you run `snapper list` in your just installed system, you should see something like this:

```
# snapper list
# | Type | Pre # | Date | User | Used Space | Cleanup | Description | Userdata
---+--------+-------+--------------------------+------+------------+---------+-----------------------+--------------
0 | single | | | root | | | current |
1* | single | | Fri Feb 18 06:06:17 2022 | root | 13.22 MiB | | first root filesystem |
2 | single | | Fri Feb 18 06:12:22 2022 | root | 3.01 MiB | number | after installation | important=yes
```

Which is the role of each "snapshot"?

* Snapshot 0: it is created by Snapper during the initialization.
* Snapshot 1: it is the *current snapshot*. It is mounted as read/write and it is where your system
lives.
* Snapshot 2: YaST created this snapshot at the end of the installation.

Your system runs on *snapshot 1*, and *snapshot 2* is just a way to travel back in time to the end
of the installation. We usually think about snapshots as *pictures* of the system on a given time,
but that is not an accurate definition.

### Offline Upgrade

During offline upgrade, YaST takes two different snapshots: one at the beginning of the installation
and another one at the end. Both snapshots are related as you can see in the table below:

```
# snapper list
# | Type | Pre # | Date | User | Used Space | Cleanup | Description | Userdata
---+--------+-------+--------------------------+------+------------+---------+-----------------------+--------------
0 | single | | | root | | | current |
1* | single | | Fri Feb 18 06:06:17 2022 | root | 13.22 MiB | | first root filesystem |
2 | single | | Fri Feb 18 06:12:22 2022 | root | 3.01 MiB | number | after installation | important=yes
3 | pre | | Fri Feb 18 10:40:05 2022 | root | 36.63 MiB | number | zypp(zypper) | important=yes
4 | post | 3 | Fri Feb 18 10:55:11 2022 | root | 16.11 MiB | number | | important=yes
5 | pre | | Fri Feb 18 13:36:49 2022 | root | 14.91 MiB | number | before update | important=yes
6 | post | 5 | Fri Feb 18 14:01:59 2022 | root | 2.45 MiB | number | after update | important=yes
```

According to the table above, YaST created snapshot 5 (*pre*) before starting the upgrade process
and snapshot 6 (*post*) at the end. However, snapshot 1 is still the root file system.

## Transactional systems

Transactional systems, like MicroOS or the *Transactional Server* role of openSUSE Tumbleweed, take
care of their own snapshots. In those cases, YaST does not perform any snapshot at the end of the
installation. About the offline upgrade, using YaST to upgrade those systems is not supported.

Starting in yast2-installation 4.3.45, YaST detects if it is installing a transactional system by
checking whether the root filesystem is mounted as read-only.

## Related features

[FATE#317932](https://w3.suse.de/~lpechacek/fate-archive/317973.html) and
[SLE-22560](https://jira.suse.com/browse/SLE-22560).
8 changes: 8 additions & 0 deletions package/yast2-installation.changes
@@ -1,3 +1,11 @@
-------------------------------------------------------------------
Sun Feb 20 09:35:26 UTC 2022 - Imobach Gonzalez Sosa <igonzalezsosa@suse.com>

- Do not create a Btrfs snapshot at the end of the installation
or upgrade when the root filesystem is mounted as read-only
(jsc#SLE-22560).
- 4.4.42

-------------------------------------------------------------------
Fri Feb 18 08:47:35 UTC 2022 - Knut Anderssen <kanderssen@suse.com>

Expand Down
3 changes: 1 addition & 2 deletions package/yast2-installation.spec
Expand Up @@ -15,9 +15,8 @@
# Please submit bugfixes or comments via https://bugs.opensuse.org/
#


Name: yast2-installation
Version: 4.4.41
Version: 4.4.42
Release: 0
Summary: YaST2 - Installation Parts
License: GPL-2.0-only
Expand Down
36 changes: 27 additions & 9 deletions src/lib/installation/snapshots_finish.rb
Expand Up @@ -2,6 +2,7 @@
require "yast2/fs_snapshot"
require "yast2/fs_snapshot_store"
require "installation/finish_client"
require "y2storage"

module Installation
class SnapshotsFinish < ::Installation::FinishClient
Expand Down Expand Up @@ -29,16 +30,21 @@ def initialize
def write
snapper_config

if !InstFunctions.second_stage_required? && Yast2::FsSnapshot.configured?
log.info("Creating root filesystem snapshot")
if Mode.update
create_post_snapshot
else
create_single_snapshot
end
skip_reason = nil
skip_reason = "no second stage" if InstFunctions.second_stage_required?
skip_reason = "snapper is not configured" unless Yast2::FsSnapshot.configured?
skip_reason = "root file system is read-only" if ro_root_fs?

if skip_reason
log.info("Skipping root filesystem snapshot creation: #{skip_reason}")
return false
end

log.info("Creating root filesystem snapshot")
if Mode.update
create_post_snapshot
else
log.info("Skipping root filesystem snapshot creation")
false
create_single_snapshot
end
end

Expand Down Expand Up @@ -78,5 +84,17 @@ def snapper_config
log.info("There is no need to configure Snapper")
end
end

# Determines whether the root filesystem is mounted as read-only
#
# @return [Boolean] true if it is mounted as read-only; false if it is not
# mounted as read-only or if it is not found
def ro_root_fs?
staging = Y2Storage::StorageManager.instance.staging
root_fs = Y2Storage::MountPoint.find_by_path(staging, "/").first
return false unless root_fs

root_fs.mount_options.include?("ro")
end
end
end
30 changes: 30 additions & 0 deletions test/snapshots_finish_test.rb
Expand Up @@ -17,12 +17,24 @@
allow(Yast2::FsSnapshot).to receive(:configured?).and_return(snapper_configured)
allow(Yast::Mode).to receive(:installation).and_return(mode == :installation)
allow(Yast2::FsSnapshot).to receive(:configure_on_install?).and_return configure
allow(Y2Storage::StorageManager).to receive(:instance).and_return(storage_manager)
allow(Y2Storage::MountPoint).to receive(:find_by_path).with(staging, "/")
.and_return([root_fs])
end

let(:second_stage_required) { false }
let(:snapper_configured) { false }
let(:mode) { :normal }
let(:configure) { false }
let(:staging) { instance_double(Y2Storage::Devicegraph) }
let(:storage_manager) { instance_double(Y2Storage::StorageManager, staging: staging) }
let(:root_fs_options) { [] }

let(:root_fs) do
instance_double(
Y2Storage::Filesystems::BlkFilesystem, mount_path: "/", mount_options: root_fs_options
)
end

context "during a fresh installation" do
let(:mode) { :installation }
Expand Down Expand Up @@ -110,6 +122,15 @@
expect(subject.write).to eq(true)
end

context "and root filesystem is read-only" do
let(:root_fs_options) { ["ro"] }

it "does not create any snapshot" do
expect(Yast2::FsSnapshot).to_not receive(:create_post)
expect(subject.write).to eq(false)
end
end

context "and could not create the snapshot" do
before do
allow(Yast2::FsSnapshot).to receive(:create_post)
Expand Down Expand Up @@ -139,6 +160,15 @@
expect(subject.write).to eq(true)
end

context "and root filesystem is read-only" do
let(:root_fs_options) { ["ro"] }

it "does not create any snapshot" do
expect(Yast2::FsSnapshot).to_not receive(:create_single)
expect(subject.write).to eq(false)
end
end

context "and could not create the snapshot" do
before do
allow(Yast2::FsSnapshot).to receive(:create_single)
Expand Down

0 comments on commit 5b0df8d

Please sign in to comment.