diff --git a/mgradm/cmd/install/kubernetes/kubernetes.go b/mgradm/cmd/install/kubernetes/kubernetes.go index dd3bca7a5..2d540aae6 100644 --- a/mgradm/cmd/install/kubernetes/kubernetes.go +++ b/mgradm/cmd/install/kubernetes/kubernetes.go @@ -41,6 +41,7 @@ NOTE: installing on a remote cluster is not supported yet! flagsUpdater := func(v *viper.Viper) { flags.InstallFlags.Coco.IsChanged = v.IsSet("coco.replicas") flags.InstallFlags.HubXmlrpc.IsChanged = v.IsSet("hubxmlrpc.replicas") + flags.InstallFlags.Saline.IsChanged = v.IsSet("saline.replicas") || v.IsSet("saline.port") } return utils.CommandHelper(globalFlags, cmd, args, &flags, flagsUpdater, run) }, diff --git a/mgradm/cmd/install/podman/podman.go b/mgradm/cmd/install/podman/podman.go index 3e5b13036..cfe4e8c65 100644 --- a/mgradm/cmd/install/podman/podman.go +++ b/mgradm/cmd/install/podman/podman.go @@ -35,6 +35,7 @@ NOTE: installing on a remote podman is not supported yet! flagsUpdater := func(v *viper.Viper) { flags.InstallFlags.Coco.IsChanged = v.IsSet("coco.replicas") flags.InstallFlags.HubXmlrpc.IsChanged = v.IsSet("hubxmlrpc.replicas") + flags.InstallFlags.Saline.IsChanged = v.IsSet("saline.replicas") || v.IsSet("saline.port") } return utils.CommandHelper(globalFlags, cmd, args, &flags, flagsUpdater, run) }, diff --git a/mgradm/cmd/install/podman/podman_test.go b/mgradm/cmd/install/podman/podman_test.go index fff498ca1..dfcba96fc 100644 --- a/mgradm/cmd/install/podman/podman_test.go +++ b/mgradm/cmd/install/podman/podman_test.go @@ -44,7 +44,10 @@ func TestParamsChangedConfig(t *testing.T) { coco: replicas: 2 hubxmlrpc: - replicas: 0` + replicas: 0 +saline: + port: 8226 + replicas: 1` dir := t.TempDir() configPath := path.Join(dir, "config.yaml") @@ -59,6 +62,9 @@ hubxmlrpc: testutils.AssertTrue(t, "Coco replicas not marked as changed", flags.Coco.IsChanged) testutils.AssertEquals(t, "Hub XML-RPC API replicas badly parsed", 0, flags.HubXmlrpc.Replicas) testutils.AssertTrue(t, "Hub XML-RPC API replicas not marked as changed", flags.HubXmlrpc.IsChanged) + testutils.AssertEquals(t, "Saline replicas badly parsed", 1, flags.Saline.Replicas) + testutils.AssertEquals(t, "Saline port badly parsed", 8226, flags.Saline.Port) + testutils.AssertTrue(t, "Saline flags not marked as changed", flags.Saline.IsChanged) return nil } @@ -79,6 +85,9 @@ func TestParamsNoConfig(t *testing.T) { testutils.AssertTrue(t, "Coco replicas marked as changed", !flags.Coco.IsChanged) testutils.AssertEquals(t, "Hub XML-RPC API replicas badly parsed", 0, flags.HubXmlrpc.Replicas) testutils.AssertTrue(t, "Hub XML-RPC API replicas marked as changed", !flags.HubXmlrpc.IsChanged) + testutils.AssertEquals(t, "Saline replicas badly parsed", 0, flags.Saline.Replicas) + testutils.AssertEquals(t, "Saline port badly parsed", 8216, flags.Saline.Port) + testutils.AssertTrue(t, "Saline flags marked as changed", !flags.Saline.IsChanged) return nil } diff --git a/mgradm/cmd/install/podman/utils.go b/mgradm/cmd/install/podman/utils.go index fcf67769d..9c2fa642c 100644 --- a/mgradm/cmd/install/podman/utils.go +++ b/mgradm/cmd/install/podman/utils.go @@ -12,10 +12,12 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/spf13/cobra" + "github.com/spf13/viper" install_shared "github.com/uyuni-project/uyuni-tools/mgradm/cmd/install/shared" "github.com/uyuni-project/uyuni-tools/mgradm/shared/coco" "github.com/uyuni-project/uyuni-tools/mgradm/shared/hub" "github.com/uyuni-project/uyuni-tools/mgradm/shared/podman" + "github.com/uyuni-project/uyuni-tools/mgradm/shared/saline" "github.com/uyuni-project/uyuni-tools/shared" . "github.com/uyuni-project/uyuni-tools/shared/l10n" shared_podman "github.com/uyuni-project/uyuni-tools/shared/podman" @@ -148,6 +150,14 @@ func installForPodman( } } + if flags.Saline.Replicas > 0 { + if err := saline.SetupSalineContainer( + systemd, authFile, flags.Image.Registry, flags.Saline, flags.Image, flags.TZ, viper.GetStringSlice("podman.arg"), + ); err != nil { + return err + } + } + if flags.SSL.UseExisting() { if err := podman.UpdateSSLCertificate(cnx, &flags.SSL.Ca, &flags.SSL.Server); err != nil { return utils.Errorf(err, L("cannot update SSL certificate")) diff --git a/mgradm/cmd/install/shared/flags.go b/mgradm/cmd/install/shared/flags.go index 50d7534eb..f156b6a8b 100644 --- a/mgradm/cmd/install/shared/flags.go +++ b/mgradm/cmd/install/shared/flags.go @@ -55,6 +55,7 @@ type InstallFlags struct { Image types.ImageFlags `mapstructure:",squash"` Coco cmd_utils.CocoFlags HubXmlrpc cmd_utils.HubXmlrpcFlags + Saline cmd_utils.SalineFlags Admin apiTypes.User Organization string } @@ -188,6 +189,8 @@ func AddInstallFlags(cmd *cobra.Command) { cmd_utils.AddHubXmlrpcFlags(cmd) + cmd_utils.AddSalineFlag(cmd) + cmd.Flags().String("admin-login", "admin", L("Administrator user name")) cmd.Flags().String("admin-password", "", L("Administrator password")) cmd.Flags().String("admin-firstName", "Administrator", L("First name of the administrator")) diff --git a/mgradm/cmd/migrate/kubernetes/kubernetes.go b/mgradm/cmd/migrate/kubernetes/kubernetes.go index 7ed5635fe..0c72ecf2f 100644 --- a/mgradm/cmd/migrate/kubernetes/kubernetes.go +++ b/mgradm/cmd/migrate/kubernetes/kubernetes.go @@ -48,6 +48,7 @@ NOTE: migrating to a remote cluster is not supported yet! flagsUpdater := func(v *viper.Viper) { flags.MigrateFlags.Coco.IsChanged = v.IsSet("coco.replicas") flags.MigrateFlags.HubXmlrpc.IsChanged = v.IsSet("hubxmlrpc.replicas") + flags.MigrateFlags.Saline.IsChanged = v.IsSet("saline.replicas") || v.IsSet("saline.port") } return utils.CommandHelper(globalFlags, cmd, args, &flags, flagsUpdater, run) }, diff --git a/mgradm/cmd/migrate/kubernetes/kubernetes_test.go b/mgradm/cmd/migrate/kubernetes/kubernetes_test.go index 02b9412f9..50f25115b 100644 --- a/mgradm/cmd/migrate/kubernetes/kubernetes_test.go +++ b/mgradm/cmd/migrate/kubernetes/kubernetes_test.go @@ -29,6 +29,7 @@ func TestParamsParsing(t *testing.T) { args = append(args, flagstests.DBUpdateImageFlagTestArgs...) args = append(args, flagstests.CocoFlagsTestArgs...) args = append(args, flagstests.HubXmlrpcFlagsTestArgs...) + args = append(args, flagstests.SalineFlagsTestArgs...) args = append(args, flagstests.ServerHelmFlagsTestArgs...) // Test function asserting that the args are properly parsed @@ -42,6 +43,7 @@ func TestParamsParsing(t *testing.T) { flagstests.AssertDBUpgradeImageFlag(t, &flags.DBUpgradeImage) flagstests.AssertCocoFlag(t, &flags.Coco) flagstests.AssertHubXmlrpcFlag(t, &flags.HubXmlrpc) + flagstests.AssertSalineFlag(t, &flags.Saline) testutils.AssertEquals(t, "Error parsing --user", "sudoer", flags.User) flagstests.AssertServerHelmFlags(t, &flags.Helm) testutils.AssertEquals(t, "Error parsing --ssl-password", "sslsecret", flags.SSL.Password) diff --git a/mgradm/cmd/migrate/podman/podman.go b/mgradm/cmd/migrate/podman/podman.go index 73b4aeb27..2cda2a0a5 100644 --- a/mgradm/cmd/migrate/podman/podman.go +++ b/mgradm/cmd/migrate/podman/podman.go @@ -40,6 +40,7 @@ NOTE: migrating to a remote podman is not supported yet! flagsUpdater := func(v *viper.Viper) { flags.MigrateFlags.Coco.IsChanged = v.IsSet("coco.replicas") flags.MigrateFlags.HubXmlrpc.IsChanged = v.IsSet("hubxmlrpc.replicas") + flags.MigrateFlags.Saline.IsChanged = v.IsSet("saline.replicas") || v.IsSet("saline.port") } return utils.CommandHelper(globalFlags, cmd, args, &flags, flagsUpdater, run) }, diff --git a/mgradm/cmd/migrate/podman/podman_test.go b/mgradm/cmd/migrate/podman/podman_test.go index 61c2f62a5..b2eeef406 100644 --- a/mgradm/cmd/migrate/podman/podman_test.go +++ b/mgradm/cmd/migrate/podman/podman_test.go @@ -26,6 +26,7 @@ func TestParamsParsing(t *testing.T) { args = append(args, flagstests.DBUpdateImageFlagTestArgs...) args = append(args, flagstests.CocoFlagsTestArgs...) args = append(args, flagstests.HubXmlrpcFlagsTestArgs...) + args = append(args, flagstests.SalineFlagsTestArgs...) args = append(args, flagstests.PodmanFlagsTestArgs...) // Test function asserting that the args are properly parsed @@ -37,6 +38,7 @@ func TestParamsParsing(t *testing.T) { flagstests.AssertDBUpgradeImageFlag(t, &flags.DBUpgradeImage) flagstests.AssertCocoFlag(t, &flags.Coco) flagstests.AssertHubXmlrpcFlag(t, &flags.HubXmlrpc) + flagstests.AssertSalineFlag(t, &flags.Saline) testutils.AssertEquals(t, "Error parsing --user", "sudoer", flags.User) flagstests.AssertPodmanInstallFlags(t, &flags.Podman) testutils.AssertEquals(t, "Wrong FQDN", "source.fq.dn", args[0]) diff --git a/mgradm/cmd/migrate/podman/utils.go b/mgradm/cmd/migrate/podman/utils.go index a25ed963e..ff6dfa892 100644 --- a/mgradm/cmd/migrate/podman/utils.go +++ b/mgradm/cmd/migrate/podman/utils.go @@ -15,6 +15,7 @@ import ( "github.com/uyuni-project/uyuni-tools/mgradm/shared/coco" "github.com/uyuni-project/uyuni-tools/mgradm/shared/hub" "github.com/uyuni-project/uyuni-tools/mgradm/shared/podman" + "github.com/uyuni-project/uyuni-tools/mgradm/shared/saline" "github.com/uyuni-project/uyuni-tools/shared" podman_utils "github.com/uyuni-project/uyuni-tools/shared/podman" "github.com/uyuni-project/uyuni-tools/shared/types" @@ -144,6 +145,21 @@ func migrateToPodman( } } + // Prepare Saline containers + if flags.Saline.Replicas > 0 { + if err = saline.Upgrade( + systemd, authFile, flags.Image.Registry, flags.Saline, flags.Image, + extractedData.Timezone, flags.Podman.Args, + ); err != nil { + return utils.Errorf(err, L("cannot setup saline service")) + } + + err := systemd.ScaleService(flags.Saline.Replicas, podman_utils.ServerSalineService) + if err != nil { + return err + } + } + log.Info().Msg(L("Server migrated")) if err := podman_utils.EnablePodmanSocket(); err != nil { diff --git a/mgradm/cmd/migrate/shared/flags.go b/mgradm/cmd/migrate/shared/flags.go index 75b5e402d..e4b03120e 100644 --- a/mgradm/cmd/migrate/shared/flags.go +++ b/mgradm/cmd/migrate/shared/flags.go @@ -21,6 +21,7 @@ type MigrateFlags struct { Mirror string HubXmlrpc utils.HubXmlrpcFlags SCC types.SCCCredentials + Saline utils.SalineFlags } // AddMigrateFlags add migration flags to a command. @@ -32,6 +33,7 @@ func AddMigrateFlags(cmd *cobra.Command) { utils.AddDBUpgradeImageFlag(cmd) utils.AddUpgradeCocoFlag(cmd) utils.AddUpgradeHubXmlrpcFlags(cmd) + utils.AddUpgradeSalineFlag(cmd) cmd.Flags().String("user", "root", L("User on the source server. Non-root user must have passwordless sudo privileges (NOPASSWD tag in /etc/sudoers)."), ) diff --git a/mgradm/cmd/support/ptf/podman/podman.go b/mgradm/cmd/support/ptf/podman/podman.go index bbf0251f7..1728a0184 100644 --- a/mgradm/cmd/support/ptf/podman/podman.go +++ b/mgradm/cmd/support/ptf/podman/podman.go @@ -21,6 +21,7 @@ type podmanPTFFlags struct { SCC types.SCCCredentials Coco adm_utils.CocoFlags Hubxmlrpc adm_utils.HubXmlrpcFlags + Saline adm_utils.SalineFlags } func newCmd(globalFlags *types.GlobalFlags, run utils.CommandFunc[podmanPTFFlags]) *cobra.Command { diff --git a/mgradm/cmd/support/ptf/podman/utils.go b/mgradm/cmd/support/ptf/podman/utils.go index e7b9eaee0..3e5ff3655 100644 --- a/mgradm/cmd/support/ptf/podman/utils.go +++ b/mgradm/cmd/support/ptf/podman/utils.go @@ -44,7 +44,7 @@ func ptfForPodman( return err } - return podman.Upgrade(systemd, authFile, "", flags.Image, dummyImage, flags.Coco, flags.Hubxmlrpc) + return podman.Upgrade(systemd, authFile, "", flags.Image, dummyImage, flags.Coco, flags.Hubxmlrpc, flags.Saline) } // variables for unit testing. diff --git a/mgradm/cmd/uninstall/podman.go b/mgradm/cmd/uninstall/podman.go index a2b1a4b38..9b91be50e 100644 --- a/mgradm/cmd/uninstall/podman.go +++ b/mgradm/cmd/uninstall/podman.go @@ -26,6 +26,7 @@ func uninstallForPodman( podman.GetServiceImage(podman.ServerService), podman.GetServiceImage(podman.ServerAttestationService + "@"), podman.GetServiceImage(podman.HubXmlrpcService), + podman.GetServiceImage(podman.ServerSalineService + "@"), } // Uninstall the service @@ -35,6 +36,7 @@ func uninstallForPodman( systemd.UninstallInstantiatedService(podman.ServerAttestationService, !flags.Force) systemd.UninstallInstantiatedService(podman.HubXmlrpcService, !flags.Force) + systemd.UninstallInstantiatedService(podman.ServerSalineService, !flags.Force) // Remove the volumes if flags.Purge.Volumes { diff --git a/mgradm/cmd/upgrade/kubernetes/kubernetes.go b/mgradm/cmd/upgrade/kubernetes/kubernetes.go index 3b0aac6d6..1335a7564 100644 --- a/mgradm/cmd/upgrade/kubernetes/kubernetes.go +++ b/mgradm/cmd/upgrade/kubernetes/kubernetes.go @@ -32,6 +32,7 @@ func newCmd(globalFlags *types.GlobalFlags, run utils.CommandFunc[kubernetesUpgr flagsUpdater := func(v *viper.Viper) { flags.UpgradeFlags.Coco.IsChanged = v.IsSet("coco.replicas") flags.UpgradeFlags.HubXmlrpc.IsChanged = v.IsSet("hubxmlrpc.replicas") + flags.UpgradeFlags.Saline.IsChanged = v.IsSet("saline.replicas") || v.IsSet("saline.port") } return utils.CommandHelper(globalFlags, cmd, args, &flags, flagsUpdater, run) }, diff --git a/mgradm/cmd/upgrade/kubernetes/kubernetes_test.go b/mgradm/cmd/upgrade/kubernetes/kubernetes_test.go index 5fefb4602..74e7367d3 100644 --- a/mgradm/cmd/upgrade/kubernetes/kubernetes_test.go +++ b/mgradm/cmd/upgrade/kubernetes/kubernetes_test.go @@ -22,6 +22,7 @@ func TestParamsParsing(t *testing.T) { args = append(args, flagstests.DBUpdateImageFlagTestArgs...) args = append(args, flagstests.CocoFlagsTestArgs...) args = append(args, flagstests.HubXmlrpcFlagsTestArgs...) + args = append(args, flagstests.SalineFlagsTestArgs...) args = append(args, flagstests.SCCFlagTestArgs...) args = append(args, flagstests.ServerHelmFlagsTestArgs...) @@ -33,6 +34,7 @@ func TestParamsParsing(t *testing.T) { flagstests.AssertDBUpgradeImageFlag(t, &flags.DBUpgradeImage) flagstests.AssertCocoFlag(t, &flags.Coco) flagstests.AssertHubXmlrpcFlag(t, &flags.HubXmlrpc) + flagstests.AssertSalineFlag(t, &flags.Saline) // TODO Assert SCC flags flagstests.AssertServerHelmFlags(t, &flags.Helm) return nil diff --git a/mgradm/cmd/upgrade/podman/podman.go b/mgradm/cmd/upgrade/podman/podman.go index 72139d807..2b2793720 100644 --- a/mgradm/cmd/upgrade/podman/podman.go +++ b/mgradm/cmd/upgrade/podman/podman.go @@ -31,6 +31,7 @@ func newCmd(globalFlags *types.GlobalFlags, run utils.CommandFunc[podmanUpgradeF flagsUpdater := func(v *viper.Viper) { flags.UpgradeFlags.Coco.IsChanged = v.IsSet("coco.replicas") flags.UpgradeFlags.HubXmlrpc.IsChanged = v.IsSet("hubxmlrpc.replicas") + flags.UpgradeFlags.Saline.IsChanged = v.IsSet("saline.replicas") || v.IsSet("saline.port") } return utils.CommandHelper(globalFlags, cmd, args, &flags, flagsUpdater, run) }, diff --git a/mgradm/cmd/upgrade/podman/podman_test.go b/mgradm/cmd/upgrade/podman/podman_test.go index 42251f804..ef802c5b5 100644 --- a/mgradm/cmd/upgrade/podman/podman_test.go +++ b/mgradm/cmd/upgrade/podman/podman_test.go @@ -20,6 +20,7 @@ func TestParamsParsing(t *testing.T) { args = append(args, flagstests.DBUpdateImageFlagTestArgs...) args = append(args, flagstests.CocoFlagsTestArgs...) args = append(args, flagstests.HubXmlrpcFlagsTestArgs...) + args = append(args, flagstests.SalineFlagsTestArgs...) args = append(args, flagstests.SCCFlagTestArgs...) args = append(args, flagstests.PodmanFlagsTestArgs...) @@ -29,6 +30,7 @@ func TestParamsParsing(t *testing.T) { flagstests.AssertDBUpgradeImageFlag(t, &flags.DBUpgradeImage) flagstests.AssertCocoFlag(t, &flags.Coco) flagstests.AssertHubXmlrpcFlag(t, &flags.HubXmlrpc) + flagstests.AssertSalineFlag(t, &flags.Saline) flagstests.AssertSCCFlag(t, &flags.SCC) flagstests.AssertPodmanInstallFlags(t, &flags.Podman) return nil diff --git a/mgradm/cmd/upgrade/podman/utils.go b/mgradm/cmd/upgrade/podman/utils.go index 9fc192ff8..3a886aa20 100644 --- a/mgradm/cmd/upgrade/podman/utils.go +++ b/mgradm/cmd/upgrade/podman/utils.go @@ -28,6 +28,6 @@ func upgradePodman(_ *types.GlobalFlags, flags *podmanUpgradeFlags, _ *cobra.Com defer cleaner() return podman.Upgrade( - systemd, authFile, flags.Image.Registry, flags.Image, flags.DBUpgradeImage, flags.Coco, flags.HubXmlrpc, + systemd, authFile, flags.Image.Registry, flags.Image, flags.DBUpgradeImage, flags.Coco, flags.HubXmlrpc, flags.Saline, ) } diff --git a/mgradm/cmd/upgrade/shared/flags.go b/mgradm/cmd/upgrade/shared/flags.go index d11046cde..218582c6e 100644 --- a/mgradm/cmd/upgrade/shared/flags.go +++ b/mgradm/cmd/upgrade/shared/flags.go @@ -16,6 +16,7 @@ type UpgradeFlags struct { DBUpgradeImage types.ImageFlags `mapstructure:"dbupgrade"` Coco utils.CocoFlags HubXmlrpc utils.HubXmlrpcFlags + Saline utils.SalineFlags } // AddUpgradeFlags add upgrade flags to a command. @@ -26,6 +27,7 @@ func AddUpgradeFlags(cmd *cobra.Command) { utils.AddUpgradeCocoFlag(cmd) utils.AddUpgradeHubXmlrpcFlags(cmd) + utils.AddUpgradeSalineFlag(cmd) } // AddUpgradeListFlags add upgrade list flags to a command. diff --git a/mgradm/shared/podman/podman.go b/mgradm/shared/podman/podman.go index 73a6d2adc..1188ce3a3 100644 --- a/mgradm/shared/podman/podman.go +++ b/mgradm/shared/podman/podman.go @@ -15,8 +15,10 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "github.com/spf13/viper" "github.com/uyuni-project/uyuni-tools/mgradm/shared/coco" "github.com/uyuni-project/uyuni-tools/mgradm/shared/hub" + "github.com/uyuni-project/uyuni-tools/mgradm/shared/saline" "github.com/uyuni-project/uyuni-tools/mgradm/shared/templates" adm_utils "github.com/uyuni-project/uyuni-tools/mgradm/shared/utils" "github.com/uyuni-project/uyuni-tools/shared" @@ -363,6 +365,7 @@ func Upgrade( upgradeImage types.ImageFlags, cocoFlags adm_utils.CocoFlags, hubXmlrpcFlags adm_utils.HubXmlrpcFlags, + salineFlags adm_utils.SalineFlags, ) error { if err := CallCloudGuestRegistryAuth(); err != nil { return err @@ -455,6 +458,12 @@ func Upgrade( return err } + if err := saline.Upgrade(systemd, authFile, registry, salineFlags, image, + utils.GetLocalTimezone(), viper.GetStringSlice("podman.arg"), + ); err != nil { + return utils.Errorf(err, L("error upgrading saline service.")) + } + return systemd.ReloadDaemon(false) } diff --git a/mgradm/shared/saline/saline.go b/mgradm/shared/saline/saline.go new file mode 100644 index 000000000..dd236f8b8 --- /dev/null +++ b/mgradm/shared/saline/saline.go @@ -0,0 +1,139 @@ +// SPDX-FileCopyrightText: 2024 SUSE LLC +// +// SPDX-License-Identifier: Apache-2.0 + +package saline + +import ( + "fmt" + "strings" + + "github.com/rs/zerolog/log" + "github.com/uyuni-project/uyuni-tools/mgradm/shared/templates" + adm_utils "github.com/uyuni-project/uyuni-tools/mgradm/shared/utils" + . "github.com/uyuni-project/uyuni-tools/shared/l10n" + "github.com/uyuni-project/uyuni-tools/shared/podman" + "github.com/uyuni-project/uyuni-tools/shared/types" + "github.com/uyuni-project/uyuni-tools/shared/utils" +) + +// Upgrade Saline. +func Upgrade( + systemd podman.Systemd, + authFile string, + registry string, + salineFlags adm_utils.SalineFlags, + baseImage types.ImageFlags, + tz string, + podmanArgs []string, +) error { + if err := writeSalineServiceFiles( + systemd, authFile, registry, salineFlags, baseImage, tz, podmanArgs, + ); err != nil { + return err + } + + return systemd.ScaleService(salineFlags.Replicas, podman.ServerSalineService) +} + +func writeSalineServiceFiles( + systemd podman.Systemd, + authFile string, + registry string, + salineFlags adm_utils.SalineFlags, + baseImage types.ImageFlags, + tz string, + podmanArgs []string, +) error { + image := salineFlags.Image + currentReplicas := systemd.CurrentReplicaCount(podman.ServerSalineService) + log.Debug().Msgf("Current Saline replicas running are %d.", currentReplicas) + + if image.Tag == "" { + if baseImage.Tag != "" { + image.Tag = baseImage.Tag + } else { + image.Tag = "latest" + } + } + if !salineFlags.IsChanged { + log.Debug().Msg("Saline settings are not changed.") + return nil + } else if salineFlags.Replicas == 0 { + log.Debug().Msg("No Saline requested.") + return nil + } else if salineFlags.Replicas > 1 { + log.Warn().Msg(L("Multiple Saline container replicas are not currently supported, setting up only one.")) + salineFlags.Replicas = 1 + } + + salineImage, err := utils.ComputeImage(registry, baseImage.Tag, image) + if err != nil { + return utils.Errorf(err, L("failed to compute image URL")) + } + + pullEnabled := salineFlags.Replicas > 0 && salineFlags.IsChanged + + preparedImage, err := podman.PrepareImage(authFile, salineImage, baseImage.PullPolicy, pullEnabled) + if err != nil { + return err + } + + ipv6Enabled := podman.HasIpv6Enabled(podman.UyuniNetwork) + + salineData := templates.SalineServiceTemplateData{ + NamePrefix: "uyuni", + Network: podman.UyuniNetwork, + Volumes: utils.SalineVolumeMounts, + Image: preparedImage, + SalinePort: salineFlags.Port, + IPV6Enabled: ipv6Enabled, + } + + log.Info().Msg(L("Setting up Saline service")) + + if err := utils.WriteTemplateToFile(salineData, + podman.GetServicePath(podman.ServerSalineService+"@"), 0555, true); err != nil { + return utils.Errorf(err, L("failed to generate systemd service unit file")) + } + + environment := fmt.Sprintf(`Environment=UYUNI_SALINE_IMAGE=%s`, preparedImage) + + if err := podman.GenerateSystemdConfFile( + podman.ServerSalineService+"@", "generated.conf", environment, true, + ); err != nil { + return utils.Errorf(err, L("cannot generate systemd conf file")) + } + + config := fmt.Sprintf(`Environment=TZ=%s +Environment="PODMAN_EXTRA_ARGS=%s" +`, strings.TrimSpace(tz), strings.Join(podmanArgs, " ")) + + if err := podman.GenerateSystemdConfFile(podman.ServerSalineService+"@", "custom.conf", + config, false); err != nil { + return utils.Errorf(err, L("cannot generate systemd user configuration file")) + } + + if err := systemd.ReloadDaemon(false); err != nil { + return err + } + return nil +} + +// SetupSalineContainer sets up the Saline service. +func SetupSalineContainer( + systemd podman.Systemd, + authFile string, + registry string, + salineFlags adm_utils.SalineFlags, + baseImage types.ImageFlags, + tz string, + podmanArgs []string, +) error { + if err := writeSalineServiceFiles( + systemd, authFile, registry, salineFlags, baseImage, tz, podmanArgs, + ); err != nil { + return err + } + return systemd.ScaleService(salineFlags.Replicas, podman.ServerSalineService) +} diff --git a/mgradm/shared/templates/salineServiceTemplate.go b/mgradm/shared/templates/salineServiceTemplate.go new file mode 100644 index 000000000..81d75021b --- /dev/null +++ b/mgradm/shared/templates/salineServiceTemplate.go @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2024 SUSE LLC +// +// SPDX-License-Identifier: Apache-2.0 + +package templates + +import ( + "io" + "text/template" + + "github.com/uyuni-project/uyuni-tools/shared/types" +) + +const salineServiceTemplate = ` +# uyuni-server-saline.service, generated by mgradm +# Use an uyuni-server-saline.service.d/local.conf file to override +[Unit] +Description=Uyuni server Saline image container service +Wants=network.target uyuni-server.service +Requires=uyuni-server.service +After=network-online.target uyuni-server.service +RequiresMountsFor=%t/containers + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +ExecStartPre=/bin/rm -f %t/uyuni-server-saline-%i.pid %t/%n.ctr-id +ExecStartPre=/usr/bin/podman rm --ignore --force -t 10 {{ .NamePrefix }}-server-saline-%i +ExecStart=/bin/sh -c '/usr/bin/podman run \ + --conmon-pidfile %t/uyuni-server-saline-%i.pid \ + --cidfile=%t/%n-%i.ctr-id \ + --cgroups=no-conmon \ + --sdnotify=conmon \ + --security-opt label=type:container_init_t \ + -d \ + --name {{ .NamePrefix }}-saline-%i \ + --hostname {{ .NamePrefix }}-saline-%i.mgr.internal \ + --network {{ .Network }} \ + -p {{ .SalinePort }}:8216 \ + {{- if $.IPV6Enabled }} + -p [::]:{{ .SalinePort }}:8216 \ + {{- end }} + {{- range .Volumes }} + -v {{ .Name }}:{{ .MountPath }}:z \ + {{- end }} + -e TZ=${TZ} \ + -e NOSSL=${NOSSL} \ + ${PODMAN_EXTRA_ARGS} ${UYUNI_SALINE_IMAGE}' +ExecStop=/usr/bin/podman stop --ignore -t 10 --cidfile=%t/%n-%i.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore -t 10 --cidfile=%t/%n-%i.ctr-id +PIDFile=%t/uyuni-server-saline-%i.pid +TimeoutStopSec=20 +TimeoutStartSec=10 +Type=forking + +[Install] +WantedBy=multi-user.target default.target +` + +// SalineServiceTemplateData holds information to create systemd file for saline container. +type SalineServiceTemplateData struct { + NamePrefix string + Image string + Network string + SalinePort int + IPV6Enabled bool + Volumes []types.VolumeMount +} + +// Render will create the systemd configuration file. +func (data SalineServiceTemplateData) Render(wr io.Writer) error { + t := template.Must(template.New("service").Parse(salineServiceTemplate)) + return t.Execute(wr, data) +} diff --git a/mgradm/shared/utils/cmd_utils.go b/mgradm/shared/utils/cmd_utils.go index c8d617120..34ecc28dd 100644 --- a/mgradm/shared/utils/cmd_utils.go +++ b/mgradm/shared/utils/cmd_utils.go @@ -154,3 +154,26 @@ func AddUpgradeHubXmlrpcFlags(cmd *cobra.Command) { Leave it unset if you want to keep the previous number of replicas.`)) _ = utils.AddFlagToHelpGroupID(cmd, "hubxmlrpc-replicas", "hubxmlrpc-container") } + +// AddSalineFlag adds the Saline related parameters to cmd. +func AddSalineFlag(cmd *cobra.Command) { + _ = utils.AddFlagHelpGroup(cmd, &utils.Group{ID: "saline-container", Title: L("Saline Flags")}) + AddContainerImageFlags(cmd, "saline", L("Saline"), "saline-container", "server-saline") + cmd.Flags().Int("saline-replicas", 0, L(`How many replicas of the Saline container should be started +(only 0 or 1 supported for now)`)) + cmd.Flags().Int("saline-port", 8216, L("Saline port (default: 8216)")) + _ = utils.AddFlagToHelpGroupID(cmd, "saline-replicas", "saline-container") + _ = utils.AddFlagToHelpGroupID(cmd, "saline-port", "saline-container") +} + +// AddUpgradeSalineFlag adds the Saline related parameters to cmd upgrade. +func AddUpgradeSalineFlag(cmd *cobra.Command) { + _ = utils.AddFlagHelpGroup(cmd, &utils.Group{ID: "saline-container", Title: L("Saline Flags")}) + AddContainerImageFlags(cmd, "saline", L("Saline"), "saline-container", "server-saline") + cmd.Flags().Int("saline-replicas", 0, L(`How many replicas of the Saline container should be started. +Leave it unset if you want to keep the previous number of replicas. +(only 0 or 1 supported for now)`)) + cmd.Flags().Int("saline-port", 8216, L("Saline port (default: 8216)")) + _ = utils.AddFlagToHelpGroupID(cmd, "saline-replicas", "saline-container") + _ = utils.AddFlagToHelpGroupID(cmd, "saline-port", "saline-container") +} diff --git a/mgradm/shared/utils/types.go b/mgradm/shared/utils/types.go index b8c71bbea..92d8d2d23 100644 --- a/mgradm/shared/utils/types.go +++ b/mgradm/shared/utils/types.go @@ -34,3 +34,11 @@ type CocoFlags struct { Image types.ImageFlags `mapstructure:",squash"` IsChanged bool } + +// SalineFlags contains settings for Saline container. +type SalineFlags struct { + Port int + Replicas int + Image types.ImageFlags `mapstructure:",squash"` + IsChanged bool +} diff --git a/shared/podman/systemd.go b/shared/podman/systemd.go index 943428aa9..42c78e597 100644 --- a/shared/podman/systemd.go +++ b/shared/podman/systemd.go @@ -28,6 +28,9 @@ const ServerAttestationService = "uyuni-server-attestation" // HubXmlrpcService is the name of the systemd service for the Hub XMLRPC container. const HubXmlrpcService = "uyuni-hub-xmlrpc" +// ServerSalineService is the name of the systemd service for the saline container. +const ServerSalineService = "uyuni-server-saline" + // ProxyService is the name of the systemd service for the proxy. const ProxyService = "uyuni-proxy-pod" diff --git a/shared/testutils/flagstests/mgradm.go b/shared/testutils/flagstests/mgradm.go index d0b5ee6d0..51eb04b13 100644 --- a/shared/testutils/flagstests/mgradm.go +++ b/shared/testutils/flagstests/mgradm.go @@ -109,3 +109,20 @@ func AssertHubXmlrpcFlag(t *testing.T, flags *utils.HubXmlrpcFlags) { testutils.AssertEquals(t, "Error parsing --hubxmlrpc-replicas", 1, flags.Replicas) testutils.AssertTrue(t, "Hub should be changed", flags.IsChanged) } + +// SalineoFlagsTestArgs is the expected values for AssertSalineFlag. +var SalineFlagsTestArgs = []string{ + "--saline-image", "salineimg", + "--saline-tag", "salinetag", + "--saline-replicas", "1", + "--saline-port", "8226", +} + +// AssertSalineFlag asserts that all saline flags are parsed correctly. +func AssertSalineFlag(t *testing.T, flags *utils.SalineFlags) { + testutils.AssertEquals(t, "Error parsing --saline-image", "salineimg", flags.Image.Name) + testutils.AssertEquals(t, "Error parsing --saline-tag", "salinetag", flags.Image.Tag) + testutils.AssertEquals(t, "Error parsing --saline-replicas", 1, flags.Replicas) + testutils.AssertEquals(t, "Error parsing --saline-port", 8226, flags.Port) + testutils.AssertTrue(t, "Saline should be changed", flags.IsChanged) +} diff --git a/shared/testutils/flagstests/mgradm_install.go b/shared/testutils/flagstests/mgradm_install.go index 7cd401223..44abb6c0e 100644 --- a/shared/testutils/flagstests/mgradm_install.go +++ b/shared/testutils/flagstests/mgradm_install.go @@ -52,6 +52,7 @@ var InstallFlagsTestArgs = func() []string { args = append(args, ImageFlagsTestArgs...) args = append(args, CocoFlagsTestArgs...) args = append(args, HubXmlrpcFlagsTestArgs...) + args = append(args, SalineFlagsTestArgs...) args = append(args, SSLGenerationFlagsTestArgs...) return args @@ -97,4 +98,5 @@ func AssertInstallFlags(t *testing.T, flags *shared.InstallFlags) { AssertImageFlag(t, &flags.Image) AssertCocoFlag(t, &flags.Coco) AssertHubXmlrpcFlag(t, &flags.HubXmlrpc) + AssertSalineFlag(t, &flags.Saline) } diff --git a/shared/utils/volumes.go b/shared/utils/volumes.go index 4181a8bda..dfab57905 100644 --- a/shared/utils/volumes.go +++ b/shared/utils/volumes.go @@ -78,6 +78,7 @@ var ServerVolumeMounts = append([]types.VolumeMount{ {MountPath: "/srv/spacewalk", Name: "srv-spacewalk"}, {MountPath: "/root", Name: "root"}, {MountPath: "/etc/pki/trust/anchors", Name: "ca-cert"}, + {MountPath: "/run/salt/master", Name: "run-salt-master"}, }, etcAndPgsqlVolumeMounts[:]...) // ServerVolumes match the volume with Persistent Volume Claim. @@ -96,6 +97,7 @@ var ServerVolumes = append([]types.Volume{ {Name: "srv-spacewalk", PersistentVolumeClaim: &types.PersistentVolumeClaim{ClaimName: "srv-spacewalk"}}, {Name: "root", PersistentVolumeClaim: &types.PersistentVolumeClaim{ClaimName: "root"}}, {Name: "ca-cert", PersistentVolumeClaim: &types.PersistentVolumeClaim{ClaimName: "ca-cert"}}, + {Name: "run-salt-master", PersistentVolumeClaim: &types.PersistentVolumeClaim{ClaimName: "run-salt-master"}}, }, etcAndPgsqlVolumes[:]...) // HubXmlrpcVolumeMounts represents volumes used by Hub Xmlrpc container. @@ -103,6 +105,13 @@ var HubXmlrpcVolumeMounts = []types.VolumeMount{ {MountPath: "/etc/pki/trust/anchors", Name: "ca-cert"}, } +// SalineVolumeMounts represents volumes used by Saline container. +var SalineVolumeMounts = []types.VolumeMount{ + {Name: "etc-salt", MountPath: "/etc/salt"}, + {Name: "etc-tls", MountPath: "/etc/pki/tls"}, + {Name: "run-salt-master", MountPath: "/run/salt/master"}, +} + // ProxyHttpdVolumes volumes used by HTTPD in proxy. var ProxyHttpdVolumes = []types.VolumeMount{ {Name: "uyuni-proxy-rhn-cache", MountPath: "/var/cache/rhn:z"}, diff --git a/uyuni-tools.changes.vzhestkov.add-saline b/uyuni-tools.changes.vzhestkov.add-saline new file mode 100644 index 000000000..f06823c63 --- /dev/null +++ b/uyuni-tools.changes.vzhestkov.add-saline @@ -0,0 +1 @@ +- Add initial support for Saline container deployment