Skip to content
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

Offer rollback option for interactive upgrading #11771

Merged
merged 19 commits into from
Feb 23, 2024

Conversation

nicok1
Copy link
Contributor

@nicok1 nicok1 commented Jan 19, 2024

Summary
This pull request introduces an update to the evcc upgrade process (e.g., via apt-get) to include a rollback option in interactive environments. This enhancement ensures a safer upgrade experience by validating the compatibility of existing configurations with the new evcc binary.

Implementation Details
Pre-Install Script: During the pre-install phase, if an evcc binary exists, it is backed up to /tmp/oldevcc.
Post-Install Script: In interactive mode, if /tmp/oldevcc contains a backup, the post-install script employs the new checkconfig command to test the configuration with the updated binary.
User Choice on Failure: If checkconfig fails, indicating an incompatibility, the user is prompted with a choice: proceed with the installation despite the incompatibility or rollback to the old binary.
Rollback Mechanism: If the user opts for rollback, the script restores the old evcc binary from /tmp/oldevcc, effectively undoing the upgrade.

Questions

  • Are there any problems with this approach?
  • What about non-interactive upgrades?

I would be happy to receive feedback and support.

@andig
Copy link
Member

andig commented Jan 20, 2024

@nicok1 das diff is leider kaum lesbar- warum sind post installs angepasst? Könntest Du den PR so anpassen, dass das echte Delta erkennbar wird?

@nicok1
Copy link
Contributor Author

nicok1 commented Jan 20, 2024

@andig
Wenn du in der diff View "Hide whitespace" aktivierst, sieht der Diff korrekt und lesbar aus. Ich weiß nicht, warum git das hier nur mit dieser Einstellung richtig anzeigen kann. Zumindest in meiner IDE wird das Ganze verständlich angezeigt.

Im Post Install Skript ist die neue evcc Binary bereits am entsprechenden Pfad vorhanden. Diese Datei wird ja benötigt, um die bestehende Config auf die neue Version zu prüfen. Deshalb wird dort bei einem Fehler der Nutzer gefragt und dann gegebenenfalls die alte Version aus dem /tmp Verzeichnis zurück kopiert und der Post Install schlägt fehl.

@StefanSchoof
Copy link
Contributor

Ich würde ein Change an den Line Ending als Ursache für das große diff vermuten

@nicok1
Copy link
Contributor Author

nicok1 commented Jan 21, 2024

Hallo StefanSchoof,
du hast recht. Danke für den Tipp. Ich hatte die Änderungen mit VS Code in WSL vorgenommen, aber VS Code hat CRLF als Line Ending verwendet. Es tut mir leid für die Unannehmlichkeiten. Ich bin offensichtlich noch nicht so erfahren. Jetzt sollte es leichter sein, die Änderungen zu betrachten und zu diskutieren.

cmd/check_config.go Outdated Show resolved Hide resolved
cmd/check_config.go Outdated Show resolved Hide resolved
cmd/check_config.go Outdated Show resolved Hide resolved
cmd/check_config.go Outdated Show resolved Hide resolved
@StefanSchoof
Copy link
Contributor

Wenn man ein revert macht, wird die Versionsnummer bei dpkg und von den evcc binary sich unterscheiden. Ich weiß gerade nicht, ob das nur kosmetisch ist oder irgendwie zu Problemen führen kann.

@nicok1
Copy link
Contributor Author

nicok1 commented Jan 21, 2024

Das Skript gibt ja Exitcode 1 zurück, falls der Nutzer ein Rollback wünscht.
Ich habe jetzt das hier gelesen: https://www.debian.org/doc/debian-policy/ch-maintainerscripts#details-of-unpack-phase-of-installation-or-upgrade

Nach meinem Verständnis würde der Package Manager versuchen, das Upgrade rückabzuwickeln auf die alte Version und ein eigenes Kopieren ins /tmp-Verzeichnis der Binary und zurück wäre eigentlich nicht zwingend erforderlich. Ich fand es aber zugegebenermaßen nicht so leicht zu verstehen und wäre froh darüber, wenn jemand das ebenfalls noch mal durchliest.

Co-authored-by: andig <cpuidle@gmx.de>
@GrimmiMeloni GrimmiMeloni self-requested a review January 22, 2024 05:55
@StefanSchoof
Copy link
Contributor

Ist das nicht postinstall https://www.debian.org/doc/debian-policy/ch-maintainerscripts#details-of-configuration und somit

If the configuration fails, the package is in a “Half-Configured” state, and an error message is generated.

Hast du mal ein package gebaut und ausprobiert, wie das verhalten ist?

@andig andig added the infrastructure Basic functionality label Jan 22, 2024
Copy link
Collaborator

@GrimmiMeloni GrimmiMeloni left a comment

Choose a reason for hiding this comment

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

Overall not sure, if the manual saving (and restoring) of the evcc binary is the way to go. But my Debian package skills are nearly nil - so I focussed on the Shell scripting side.

fi

if [ -f /tmp/oldevcc ] && [ $INTERACTIVE -eq 1 ]; then
checkConfigOutput=$(/usr/bin/evcc checkconfig 2>&1 || true)
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's the point of the OR with true here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for your feedback. The modification to this line is intended to prevent the package manager from halting the script execution if the subcommand returns a non-zero exit code. By appending '|| true'.

I have run some tests on the behaviour of the package manager that we can discuss (as soon as I have more time). This may result in a few more changes.

Copy link
Collaborator

@GrimmiMeloni GrimmiMeloni Jan 25, 2024

Choose a reason for hiding this comment

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

OK interesting - especially the part about the subcommand / subshell RC influencing postinstall.sh. I would fail to see how postinstall.sh can even know about any RC from the variable substitution as it does not capture it. But I'll take your word for it.
Feel free to mark this as resolved and to ping me again on this subject once you had the time to fiddle around more, and want to bounce around ideas.

checkConfigOutput=$(/usr/bin/evcc checkconfig 2>&1 || true)
oldevccversion=$(/tmp/oldevcc -v)
newevccversion=$(/usr/bin/evcc -v)
if echo "$checkConfigOutput" | grep -q "config valid"; then
Copy link
Collaborator

Choose a reason for hiding this comment

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

if [ "$checkConfigOutput" = "config valid" ]

Copy link
Member

Choose a reason for hiding this comment

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

Solche Änderungen kannst Du auch direkt inline zum applien vorschlagen ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That makes no sense at this point. If successful, the checkconfig command does not return exactly "config valid", but also dynamic information such as the version number. It should therefore be checked for contains and not for equals.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sorry about that. I checked what the command does, but apparently I did not look close enough. My bad.


if [ "$oldevccversion" = "$newevccversion" ]; then
echo "--------------------------------------------------------------------------------"
echo "Old evcc version detected. To apply the new version, please reinstall evcc. (e.g. apt-get reinstall evcc)"
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is somewhat confusing.
The check is for old and new version being equal, yet, the log message claims and old version was detected.
What does "old version" in this context mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I understand that. To explain it: If the version in /tmp/oldevcc is the same as /usr/bin/evcc, then a rollback was done before and then the apt-get install command was used (again). In order to really get the latest binary after correcting the config, either a newer release must be installed with apt-get install, or the current release must be reinstalled with apt-get reinstall. Perhaps you have an idea for a more suitable feedback?

Here is an example of dpkg -s evcc after a rollback:

klein@NPC:~/evcc$ dpkg -s evcc
Package: evcc
Status: install ok half-configured
Priority: optional
Installed-Size: 78064
Maintainer: andig <cpuidle@gmx.de>
Architecture: amd64
Version: 0.123.9~next ----> But Binary is 0.123.8
Config-Version: 0.123.8 
Description: EV Charge Controller
Homepage: https://evcc.io

Copy link
Collaborator

Choose a reason for hiding this comment

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

Now I'm even more confused. 😅
Does the above dpkg output indicate, that the rollback does not actually roll back, in the sense that while it did restore the old version (0.123.8), the package manager now thinks 0.123.9 is already installed?
If so, that sounds like a total nightmare/deal breaker. 😢

else
if [ "$oldevccversion" = "$newevccversion" ]; then
echo "--------------------------------------------------------------------------------"
echo "Old evcc version detected. To apply the new version, please reinstall evcc. (e.g. apt-get reinstall evcc)"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same as above.
Also - any chance to deduplicate?

echo "$checkConfigOutput"
echo "--------------------------------------------------------------------------------"

while true; do
Copy link
Collaborator

Choose a reason for hiding this comment

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

Either this while-block is incorrectly indented, or it is accidentally added to the else branch above. (Or Github UI is messing up my view...)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The indentation issue has been resolved. However, I can confirm that the placement of the loop is correct.

@@ -99,3 +160,8 @@ if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-decon
fi
fi
fi

# Fail installation if checkconfig command failed and the user decided to keep the old version to inform package manager about outcome
Copy link
Collaborator

@GrimmiMeloni GrimmiMeloni Jan 24, 2024

Choose a reason for hiding this comment

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

Does it make sense to put this at the end? Assuming the config check failed, the above lines would still try to activate/restart the currently running service instance. 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The idea is that the service should continue to use the old binary in the event of an invalid config and the user's rollback request, and that the service continues to function as a result. Exit code 1 ensures that the package manager recognises the installation as not completely successful and remembers the last working config/version. Alternatively, the user can choose to use a non-functioning newer version, as was previously the case by default.

@@ -101,4 +101,9 @@ if [ "$1" = "upgrade" ]; then
copyDbToUserDir
fi

# copy current binary to /tmp/oldevcc (for possibility to rollback)
if [ -f /usr/bin/evcc ]; then
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd use the "$1" = upgrade check here, just to stay within the overall "standards" this script tries to apply.
Simply moving the cp into the block above below copyDbToUserDir should do the trick.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's right. That should work. Thank you. I will apply it.

@nicok1
Copy link
Contributor Author

nicok1 commented Jan 25, 2024

Ist das nicht postinstall https://www.debian.org/doc/debian-policy/ch-maintainerscripts#details-of-configuration und somit

If the configuration fails, the package is in a “Half-Configured” state, and an error message is generated.

Hast du mal ein package gebaut und ausprobiert, wie das verhalten ist?

Ich habe eine Hand voll Tests ausgeführt. So sieht es nach einem Rollback aus:

klein@NPC:~/evcc$ dpkg -s evcc
Package: evcc
Status: install ok half-configured
Priority: optional
Installed-Size: 78064
Maintainer: andig <cpuidle@gmx.de>
Architecture: amd64
Version: 0.123.9~next ----> But Binary is 0.123.8
Config-Version: 0.123.8 
Description: EV Charge Controller
Homepage: https://evcc.io

Du hast also recht.
Die Auswirkungen durch das Rollback sind:
apt-get install evcc aktualisiert die Binary nur, wenn es seit dem letzten Rollback/Install Befehl eine neuere Version gibt.
Ansonsten muss
apt-get reinstall evcc verwendet werden, damit die neuere Binary verwendet wird.

Man könnte die neuere Binary nach einem Rollback ebenfalls speichern und könnte bei erneutem Aufruf des regulären Install Befehls die neuere Binary zurück kopieren. (Nur, wenn seitdem kein neuer Evcc Release vorliegt). So würde die Config wieder mit der neuen Version geprüft werden und es wäre wieder Erfolg/Rollback/Beibehalten als möglicher Ausgang denkbar, ohne einen anderen Befehl verwenden zu müssen. Das würde aber die Komplexität noch ein Stück weit erhöhen.

@GrimmiMeloni
Copy link
Collaborator

GrimmiMeloni commented Jan 25, 2024

Ist das nicht postinstall https://www.debian.org/doc/debian-policy/ch-maintainerscripts#details-of-configuration und somit

If the configuration fails, the package is in a “Half-Configured” state, and an error message is generated.

Hast du mal ein package gebaut und ausprobiert, wie das verhalten ist?

Ich habe eine Hand voll Tests ausgeführt. So sieht es nach einem Rollback aus:

klein@NPC:~/evcc$ dpkg -s evcc
Package: evcc
Status: install ok half-configured
Priority: optional
Installed-Size: 78064
Maintainer: andig <cpuidle@gmx.de>
Architecture: amd64
Version: 0.123.9~next ----> But Binary is 0.123.8
Config-Version: 0.123.8 
Description: EV Charge Controller
Homepage: https://evcc.io

Du hast also recht. Die Auswirkungen durch das Rollback sind: apt-get install evcc aktualisiert die Binary nur, wenn es seit dem letzten Rollback/Install Befehl eine neuere Version gibt. Ansonsten muss apt-get reinstall evcc verwendet werden, damit die neuere Binary verwendet wird.

Man könnte die neuere Binary nach einem Rollback ebenfalls speichern und könnte bei erneutem Aufruf des regulären Install Befehls die neuere Binary zurück kopieren. (Nur, wenn seitdem kein neuer Evcc Release vorliegt). So würde die Config wieder mit der neuen Version geprüft werden und es wäre wieder Erfolg/Rollback/Beibehalten als möglicher Ausgang denkbar, ohne einen anderen Befehl verwenden zu müssen. Das würde aber die Komplexität noch ein Stück weit erhöhen.

Puh. Je mehr ich mich hier reinfuchse, desto überraschter bin ich. Das hier kein Support für atomare Operationen im dpkg Standard existiert ist echt bemerkenswert. Ich dachte sowas hat man schon vor 25 Jahren gelöst.
Soweit ich es verstehe, wird die entsprechende Verantwortung dafür auf das Management Frontend (apt, yum, ...) abgewälzt. Kennt sich jemand mit diesen etwas näher aus? Falls eines dieser Frontends atomare Upgrades unterstützt, könnten wir die Installationsanleitung von evcc entsprechend anpassen, und darauf hinweisen das Upgrades via dpkg nicht empfohlen sind.

@StefanSchoof
Copy link
Contributor

Wäre "old-postrm upgrade new-version" ein besserer Platz für den config check? Wenn ich die Doku gerade verstehe sollte zu dem Moment das neue evcc binary am Platz sein und ein error unwind möglich sein, um zur alten Version per dpkg zurück zu kehren.

@GrimmiMeloni
Copy link
Collaborator

@StefanSchoof meinst Du konkret aus https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html Absatz 6.6 Punkt Nummer 5 ?

If the package is being upgraded:

Call:

old-postrm upgrade new-version

Und dann ggf. die Kaskade für den Failzweig? Das sieht zumindest nach der korrekten Stelle aus. Laut Doku ist man dort auch noch vor dem "Point of no return".

@StefanSchoof
Copy link
Contributor

Ja und man ist nach 4. wo das neue binary abgelegt wurde. Schade ist nur das es ein Skript aus dem installieren package und nicht dem neuen ist. Damit dauert es länger bis das bei Nutzern ist und wenn das Skript Probleme macht kann man nicht einfach eine neue Version mit einen Fix veröffentlichen.

@nicok1
Copy link
Contributor Author

nicok1 commented Jan 26, 2024

Danke euch beiden. Ich habe nachgedacht und verstanden, was ihr vorhabt und wie ihr euch das vorstellt. Die Frage ist, ob überhaupt die alte Datei zurück kopiert werden muss oder ob das nicht jetzt sogar dpkg inkl. richtiger Protokollierung der Version von selbst macht. Dpkg scheint ja laut Doku selbst ein temporäres Backup der alten Dateien anzulegen. Falls ja, werde ich den Code zeitnah nochmal anpassen.

@StefanSchoof
Copy link
Contributor

Damit keine Missverständnisse auf kommen: Ich bin nicht Teil des evcc Teams und somit sind die Kommentare von mir nur Hinweise. Das Team kann das anders sehen.

@andig
Copy link
Member

andig commented Jan 26, 2024

Ich hab von Paketmanagern keine Ahnung und bin für jede Hilfe dankbar- da muss ich mich voll auf Euch verlassen!

@GrimmiMeloni
Copy link
Collaborator

Damit keine Missverständnisse auf kommen: Ich bin nicht Teil des evcc Teams und somit sind die Kommentare von mir nur Hinweise. Das Team kann das anders sehen.

Ich nenne sowas "engagierter Nutzer" 😄

@GrimmiMeloni
Copy link
Collaborator

Danke euch beiden. Ich habe nachgedacht und verstanden, was ihr vorhabt und wie ihr euch das vorstellt. Die Frage ist, ob überhaupt die alte Datei zurück kopiert werden muss oder ob das nicht jetzt sogar dpkg inkl. richtiger Protokollierung der Version von selbst macht. Dpkg scheint ja laut Doku selbst ein temporäres Backup der alten Dateien anzulegen. Falls ja, werde ich den Code zeitnah nochmal anpassen.

Genau. Wenn Du in die Richtung nochmal experimentieren würdest wäre das super.

@StefanSchoof
Copy link
Contributor

Vielleicht klappt es auch in old-postrm upgrade new-version einfach nur den check config aufzurufen und die Logik mit der Frage an den Nutzer in new-postrm failed-upgrade old-version new-version zu machen. Das Skript könnte man aktualisieren falls darin ein Fehler enthalten ist und damit verhindern, dass der ganze Update fehlschlägt.

@nicok1
Copy link
Contributor Author

nicok1 commented Jan 27, 2024

Ich habe noch ein wenig getestet und das Skript angepasst. Es sieht jetzt eigentlich relativ gut aus. Falls der Nutzer ein Rollback möchte, kümmert sich dpkg darum und protokolliert am Ende auch die richtige Version ohne seltsamen Status. Wenn ein Rollback passieren soll, muss der Upgrade-Schritt scheitern sowie der "new-postrm failed-upgrade old-version new-version" Schritt. Dann folgt der "Error Unwind".

Das Kopieren der alten Version in tmp ist nicht mehr notwendig. Das ist definitiv sauberer und gefällt mir wesentlich besser.
Winziger Schönheitsfehler: dpkg macht ein paar Ausgaben und beschreibt das Fehlschlagen der beiden Schritte. Ich habe deshalb ein "Following errors are intended:" hinzugefügt.

Die beiden Schritte nochmal aufzuteilen (Test und Abfrage) hat bei mir nicht so gut geklappt.

Wenn ihr möchtet, könnt ihr ja auch mal das Verhalten testen. Am einfachsten klappt es mit dem reinstall Befehl. (geg. vorher 0.123.8 Binary entsprechend plazieren)
Sobald der Code gemergt ist, dauert es natürlich noch ein weiteres Release, bis die Nutzer davon etwas mitbekommen können, da die entsprechende Logik ja im old-postrm Teil abgelegt ist.

@GrimmiMeloni
Copy link
Collaborator

@nicok1 gab es hier nochmal positive Erfahrung? Wenn ja, dann würde ich vorschlagen das wir den PR nicht mehr als draft sehen und einen merge anstreben sollten.

@nicok1
Copy link
Contributor Author

nicok1 commented Feb 23, 2024

@GrimmiMeloni
Meine Tests waren vielversprechend. Wenn nochmal jemand anderes drüber schaut und kurz ausprobiert, dann können wir das so gerne mergen.

@nicok1 nicok1 marked this pull request as ready for review February 23, 2024 12:26
@andig andig merged commit d59986c into evcc-io:master Feb 23, 2024
@andig
Copy link
Member

andig commented Feb 23, 2024

Klasse PR, danke für Deine Geduld. Jetzt schauen wir einfach mal, wie gut das funktioniert...

@andig
Copy link
Member

andig commented Feb 28, 2024

@nicok1 could we just check for exit code instead of actual string match? Problem is that depending on log level the affirmative config valid won't even be displayed...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
infrastructure Basic functionality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants