From e4eee83d23232b341af2d7a932fb9c302fa3e2f5 Mon Sep 17 00:00:00 2001 From: Ondrej Holecek Date: Mon, 10 Jun 2024 21:54:37 +0200 Subject: [PATCH] Check if server container or volumes already exists If the volumes are already populated, try to reuse the existing user/org if it is possible. --- mgradm/cmd/install/podman/utils.go | 15 ++++-- mgradm/cmd/install/shared/shared.go | 46 +++++++++++++++++-- shared/podman/hostinspector.go | 9 +++- shared/podman/hostinspector_test.go | 3 ++ shared/test_utils/asserts.go | 7 +++ uyuni-tools.changes.oholecek.mgradm_reinstall | 1 + 6 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 uyuni-tools.changes.oholecek.mgradm_reinstall diff --git a/mgradm/cmd/install/podman/utils.go b/mgradm/cmd/install/podman/utils.go index cd2c93be6..87d022b02 100644 --- a/mgradm/cmd/install/podman/utils.go +++ b/mgradm/cmd/install/podman/utils.go @@ -6,6 +6,7 @@ package podman import ( "errors" + "fmt" "os/exec" "strings" @@ -43,11 +44,6 @@ func installForPodman( cmd *cobra.Command, args []string, ) error { - flags.CheckParameters(cmd, "podman") - if _, err := exec.LookPath("podman"); err != nil { - return errors.New(L("install podman before running this command")) - } - hostData, err := shared_podman.InspectHost() if err != nil { return err @@ -59,6 +55,15 @@ func installForPodman( } defer cleaner() + if hostData.HasUyuniServer { + return fmt.Errorf(L("Server is already initialized! Uninstall before attempting new installation or use upgrade command")) + } + + flags.CheckParameters(cmd, "podman") + if _, err := exec.LookPath("podman"); err != nil { + return errors.New(L("install podman before running this command")) + } + fqdn, err := utils.GetFqdn(args) if err != nil { return err diff --git a/mgradm/cmd/install/shared/shared.go b/mgradm/cmd/install/shared/shared.go index 3aa3acb17..615fe2be0 100644 --- a/mgradm/cmd/install/shared/shared.go +++ b/mgradm/cmd/install/shared/shared.go @@ -5,6 +5,8 @@ package shared import ( + "errors" + "net/url" "os" "path/filepath" "strconv" @@ -24,6 +26,13 @@ const setup_name = "setup.sh" // RunSetup execute the setup. func RunSetup(cnx *shared.Connection, flags *InstallFlags, fqdn string, env map[string]string) error { + // Containers should be running now, check storage if it is using volume from already configured server + preconfigured := false + if isServerConfigured(cnx) { + log.Warn().Msg(L("Server appears to be already configured. Installation will continue, but installation options may be ignored.")) + preconfigured = true + } + tmpFolder := generateSetupScript(flags, fqdn, env) defer os.RemoveAll(tmpFolder) @@ -46,10 +55,37 @@ func RunSetup(cnx *shared.Connection, flags *InstallFlags, fqdn string, env map[ apiCnx := api.ConnectionDetails{ Server: fqdn, Insecure: false, + User: flags.Admin.Login, + Password: flags.Admin.Password, } - _, err := org.CreateFirst(&apiCnx, flags.Organization, &flags.Admin) - if err != nil { - return err + + // Check if there is already admin user with given password and organization with same name + if _, err := api.Init(&apiCnx); err == nil { + if _, err := org.GetOrganizationDetails(&apiCnx, flags.Organization); err == nil { + log.Info().Msgf(L("Server organization already exists, reusing")) + } else { + log.Debug().Err(err).Msg("Error returned by server") + log.Warn().Msgf(L("Administration user already exists, but organization %s could not be found"), flags.Organization) + } + } else { + var connError *url.Error + if errors.As(err, &connError) { + // We were not able to connect to the server at all + return err + } + // We do not have any user existing, do not try to login + apiCnx = api.ConnectionDetails{ + Server: fqdn, + Insecure: false, + } + _, err := org.CreateFirst(&apiCnx, flags.Organization, &flags.Admin) + if err != nil { + if preconfigured { + log.Warn().Msgf(L("Administration user already exists, but provided credentials are not valid")) + } else { + return err + } + } } } @@ -137,3 +173,7 @@ func boolToString(value bool) string { } return "N" } + +func isServerConfigured(cnx *shared.Connection) bool { + return cnx.TestExistenceInPod("/root/.MANAGER_SETUP_COMPLETE") +} diff --git a/shared/podman/hostinspector.go b/shared/podman/hostinspector.go index 6db55862a..5aef5d783 100644 --- a/shared/podman/hostinspector.go +++ b/shared/podman/hostinspector.go @@ -28,6 +28,10 @@ func NewHostInspector(scriptDir string) HostInspector { types.NewInspectData( "scc_password", "cat /etc/zypp/credentials.d/SCCcredentials 2>&1 /dev/null | grep password | cut -d= -f2 || true"), + + types.NewInspectData( + "has_uyuni_server", + "systemctl list-unit-files uyuni-server.service >/dev/null && echo true || echo false"), }, ScriptDir: scriptDir, } @@ -38,8 +42,9 @@ func NewHostInspector(scriptDir string) HostInspector { // HostInspectData are the data returned by the host inspector. type HostInspectData struct { - SccUsername string `mapstructure:"scc_username"` - SccPassword string `mapstructure:"scc_password"` + SccUsername string `mapstructure:"scc_username"` + SccPassword string `mapstructure:"scc_password"` + HasUyuniServer bool `mapstructure:"has_uyuni_server"` } // ReadInspectData parses the data generated by the host inspector. diff --git a/shared/podman/hostinspector_test.go b/shared/podman/hostinspector_test.go index 5b8b5ed33..a2c3a623f 100644 --- a/shared/podman/hostinspector_test.go +++ b/shared/podman/hostinspector_test.go @@ -27,6 +27,7 @@ func TestHostInspectorGenerate(t *testing.T) { # inspect.sh, generated by mgradm echo "scc_username=$(cat /etc/zypp/credentials.d/SCCcredentials 2>&1 /dev/null | grep username | cut -d= -f2 || true)" >> ` + dataPath + ` echo "scc_password=$(cat /etc/zypp/credentials.d/SCCcredentials 2>&1 /dev/null | grep password | cut -d= -f2 || true)" >> ` + dataPath + ` +echo "has_uyuni_server=$(systemctl list-unit-files uyuni-server.service >/dev/null && echo true || echo false)" >> ` + dataPath + ` exit 0 ` @@ -43,6 +44,7 @@ func TestHostInspectorParse(t *testing.T) { content := ` scc_username=myuser scc_password=mysecret +has_uyuni_server=true ` test_utils.WriteFile(t, inspector.GetDataPath(), content) @@ -53,4 +55,5 @@ scc_password=mysecret test_utils.AssertEquals(t, "Invalid SCC username", "myuser", actual.SccUsername) test_utils.AssertEquals(t, "Invalid SCC password", "mysecret", actual.SccPassword) + test_utils.AssertTrue(t, "HasUyuniServer should be true", actual.HasUyuniServer) } diff --git a/shared/test_utils/asserts.go b/shared/test_utils/asserts.go index 459694896..21c5afd15 100644 --- a/shared/test_utils/asserts.go +++ b/shared/test_utils/asserts.go @@ -12,3 +12,10 @@ func AssertEquals[T comparable](t *testing.T, message string, expected T, actual t.Errorf(message+": got '%v' expected '%v'", actual, expected) } } + +// AssertTrue ensures a value is true and raises and error if not. +func AssertTrue(t *testing.T, message string, actual bool) { + if !actual { + t.Errorf(message) + } +} diff --git a/uyuni-tools.changes.oholecek.mgradm_reinstall b/uyuni-tools.changes.oholecek.mgradm_reinstall new file mode 100644 index 000000000..6e297dc93 --- /dev/null +++ b/uyuni-tools.changes.oholecek.mgradm_reinstall @@ -0,0 +1 @@ +- allow server installation with prexexisting storage volumes