Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion cmd/e2e-test/node/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func NewCreateCommand() cli.Command {
createCmd.AddPositionalValue(&cmd.instanceName, "INSTANCE_NAME", 1, true, "Name of the instance to create.")
createCmd.String(&cmd.configFile, "f", "config-file", "Path tests config file.")
createCmd.String(&cmd.credsProvider, "c", "creds-provider", "Credentials provider to use (iam-ra, ssm).")
createCmd.String(&cmd.os, "o", "os", "OS to use (al23, ubuntu2004, ubuntu2204, ubuntu2404, rhel8, rhel9, bottlerocket).")
createCmd.String(&cmd.os, "o", "os", "OS to use (al23, ubuntu2004, ubuntu2204, ubuntu2404, rhel8, rhel9, sles15sp7, sles16, bottlerocket).")
createCmd.String(&cmd.arch, "a", "arch", "Architecture to use (amd64, arm64).")
createCmd.String(&cmd.instanceSize, "s", "instance-size", "Instance size to use (Large, XLarge).")
createCmd.String(&cmd.instanceType, "t", "instance-type", "Instance type to use (t3.large, g4dn.xlarge, etc). If provided, instance size and the default instance type fallbacks are ignored.")
Expand Down Expand Up @@ -298,6 +298,14 @@ var oses = map[string]map[string]func() e2e.NodeadmOS{
return osystem.NewRedHat9ARM(os.Getenv("RHEL_USERNAME"), os.Getenv("RHEL_PASSWORD"))
},
},
"sles15sp7": {
"amd64": func() e2e.NodeadmOS { return osystem.NewSLES15SP7AMD() },
"arm64": func() e2e.NodeadmOS { return osystem.NewSLES15SP7ARM() },
},
"sles16": {
"amd64": func() e2e.NodeadmOS { return osystem.NewSLES16AMD() },
"arm64": func() e2e.NodeadmOS { return osystem.NewSLES16ARM() },
},
"bottlerocket": {
"amd64": func() e2e.NodeadmOS { return osystem.NewBottleRocket() },
"arm64": func() e2e.NodeadmOS { return osystem.NewBottleRocketARM() },
Expand Down
2 changes: 1 addition & 1 deletion internal/kubelet/kubelet.service
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Requires=containerd.service
[Service]
Slice=runtime.slice
EnvironmentFile=/etc/eks/kubelet/environment
ExecStartPre=/sbin/iptables -P FORWARD ACCEPT -w 5
ExecStartPre=iptables -P FORWARD ACCEPT -w 5
ExecStart=/usr/bin/kubelet \
--config /etc/kubernetes/kubelet/config.json \
--kubeconfig /var/lib/kubelet/kubeconfig \
Expand Down
44 changes: 43 additions & 1 deletion internal/network/proxy_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ func validatePackageManagerProxyConfig(osName string) error {
return validateYumProxyConfig(httpProxy, httpsProxy)
case system.AmazonOsName:
return validateDnfProxyConfig(httpProxy, httpsProxy)
case system.SlesOsName:
return validateZypperProxyConfig(httpProxy, httpsProxy)
default:
return fmt.Errorf("unsupported operating system: %s", osName)
}
Expand Down Expand Up @@ -180,7 +182,7 @@ func validateSSMProxyConfig(osName string) error {
switch osName {
case system.UbuntuOsName:
ssmServicePath = "/etc/systemd/system/snap.amazon-ssm-agent.amazon-ssm-agent.service.d/http-proxy.conf"
case system.RhelOsName, system.AmazonOsName:
case system.RhelOsName, system.AmazonOsName, system.SlesOsName:
ssmServicePath = "/etc/systemd/system/amazon-ssm-agent.service.d/http-proxy.conf"
default:
return fmt.Errorf("unsupported operating system: %s", osName)
Expand Down Expand Up @@ -284,6 +286,46 @@ func validateDnfProxyConfig(httpProxy, httpsProxy string) error {
return nil
}

// validateZypperProxyConfig validates the zypper proxy configuration.
// SUSE's system-wide proxy convention is /etc/sysconfig/proxy (unlike
// yum/dnf, zypper has no manager-specific proxy.conf)
func validateZypperProxyConfig(httpProxy, httpsProxy string) error {
proxyConfPath := "/etc/sysconfig/proxy"
if !fileExists(proxyConfPath) {
return validation.WithRemediation(
fmt.Errorf("proxy configuration file not found: %s", proxyConfPath),
fmt.Sprintf("Create the proxy configuration file at %s with the following content:\n"+
"PROXY_ENABLED=\"yes\"\n"+
"HTTP_PROXY=\"%s\"\n"+
"HTTPS_PROXY=\"%s\"",
proxyConfPath, httpProxy, httpsProxy),
)
}

content, err := os.ReadFile(proxyConfPath)
if err != nil {
return fmt.Errorf("failed to read proxy configuration file: %w", err)
}

if httpProxy != "" && !strings.Contains(string(content), fmt.Sprintf("HTTP_PROXY=\"%s\"", httpProxy)) {
return validation.WithRemediation(
fmt.Errorf("proxy configuration file does not contain correct HTTP_PROXY value"),
fmt.Sprintf("Update the proxy configuration file at %s with the correct HTTP_PROXY value: HTTP_PROXY=\"%s\"",
proxyConfPath, httpProxy),
)
}

if httpsProxy != "" && !strings.Contains(string(content), fmt.Sprintf("HTTPS_PROXY=\"%s\"", httpsProxy)) {
return validation.WithRemediation(
fmt.Errorf("proxy configuration file does not contain correct HTTPS_PROXY value"),
fmt.Sprintf("Update the proxy configuration file at %s with the correct HTTPS_PROXY value: HTTPS_PROXY=\"%s\"",
proxyConfPath, httpsProxy),
)
}

return nil
}

func getEffectiveProxyValue(upperVar, lowerVar string) string {
upperValue := os.Getenv(upperVar)
lowerValue := os.Getenv(lowerVar)
Expand Down
97 changes: 65 additions & 32 deletions internal/packagemanager/packagemanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ import (
)

const (
aptPackageManager = "apt"
snapPackageManager = "snap"
yumPackageManager = "yum"
aptPackageManager = "apt"
snapPackageManager = "snap"
yumPackageManager = "yum"
zypperPackageManager = "zypper"

snapInstallVerb = "install"
snapUpdateVerb = "refresh"
Expand All @@ -46,7 +47,7 @@ const (
ssmPkgName = "amazon-ssm-agent"
)

// DistroPackageManager defines a new package manager using apt or yum
// DistroPackageManager defines a new package manager using apt, yum, or zypper
type DistroPackageManager struct {
manager string
installVerb string
Expand Down Expand Up @@ -201,13 +202,25 @@ func (pm *DistroPackageManager) appendPackageVersion(packageName, version string
switch pm.manager {
case yumPackageManager:
return fmt.Sprintf("%s-%s", packageName, version)
case aptPackageManager:
case aptPackageManager, zypperPackageManager:
return fmt.Sprintf("%s=%s", packageName, version)
default:
return packageName
}
}

// newAutoConfirmCmd builds a package manager command that runs non-interactively.
// zypper only honors the confirm flag immediately after the subcommand
// (e.g. "zypper install -y pkg")
// Centralized here so every package getter gets the right flag placement
// without repeating the zypper special case at each call site.
func (pm *DistroPackageManager) newAutoConfirmCmd(verb, packageName string) artifact.Cmd {
if pm.manager == zypperPackageManager {
return artifact.NewCmd(pm.manager, verb, "-y", packageName)
}
return artifact.NewCmd(pm.manager, verb, packageName, "-y")
}

func (pm *DistroPackageManager) getContainerdPackageNameWithVersionConstraint(version string) string {
containerdPkgName := containerdDistroPkgName
if pm.dockerRepo != "" {
Expand All @@ -230,18 +243,18 @@ func (pm *DistroPackageManager) refreshMetadataCacheCommand(ctx context.Context)
func (pm *DistroPackageManager) GetContainerd(versionConstraint string) artifact.Package {
packageName := pm.getContainerdPackageNameWithVersionConstraint(versionConstraint)
return artifact.NewPackageSource(
artifact.NewCmd(pm.manager, pm.installVerb, packageName, "-y"),
artifact.NewCmd(pm.manager, pm.deleteVerb, packageName, "-y"),
artifact.NewCmd(pm.manager, pm.updateVerb, packageName, "-y"),
pm.newAutoConfirmCmd(pm.installVerb, packageName),
pm.newAutoConfirmCmd(pm.deleteVerb, packageName),
pm.newAutoConfirmCmd(pm.updateVerb, packageName),
)
}

// GetIptables satisfies the getiptables source interface
func (pm *DistroPackageManager) GetIptables() artifact.Package {
return artifact.NewPackageSource(
artifact.NewCmd(pm.manager, pm.installVerb, iptablesPkgName, "-y"),
artifact.NewCmd(pm.manager, pm.deleteVerb, iptablesPkgName, "-y"),
artifact.NewCmd(pm.manager, pm.updateVerb, iptablesPkgName, "-y"),
pm.newAutoConfirmCmd(pm.installVerb, iptablesPkgName),
pm.newAutoConfirmCmd(pm.deleteVerb, iptablesPkgName),
pm.newAutoConfirmCmd(pm.updateVerb, iptablesPkgName),
)
}

Expand All @@ -256,34 +269,48 @@ func (pm *DistroPackageManager) GetSSMPackage() artifact.Package {
artifact.NewCmd(snapPackageManager, snapUpdateVerb, ssmPkgName),
)
}
// SSM on SLES is not installed through zypper, so there is nothing
// for the package manager to install/remove/update.
// This matters for uninstall. yum and apt return exit 0 when removing
// a package that was never installed.
// zypper returns a non-zero exit (ZYPPER_EXIT_INF_CAP_NOT_FOUND).
// cmd.Retry has no retry limit around `nodeadm uninstall`, so a
// non-zero exit would retry forever.
if pm.manager == zypperPackageManager {
return artifact.NewPackageSource(
artifact.NewCmd("true"),
artifact.NewCmd("true"),
artifact.NewCmd("true"),
)
}
return artifact.NewPackageSource(
artifact.NewCmd(pm.manager, pm.installVerb, ssmPkgName, "-y"),
artifact.NewCmd(pm.manager, pm.deleteVerb, ssmPkgName, "-y"),
artifact.NewCmd(pm.manager, pm.updateVerb, ssmPkgName, "-y"),
pm.newAutoConfirmCmd(pm.installVerb, ssmPkgName),
pm.newAutoConfirmCmd(pm.deleteVerb, ssmPkgName),
pm.newAutoConfirmCmd(pm.updateVerb, ssmPkgName),
)
}

func (pm *DistroPackageManager) caCertsPackage() artifact.Package {
return artifact.NewPackageSource(
artifact.NewCmd(pm.manager, pm.installVerb, caCertsPkgName, "-y"),
artifact.NewCmd(pm.manager, pm.deleteVerb, caCertsPkgName, "-y"),
artifact.NewCmd(pm.manager, pm.updateVerb, caCertsPkgName, "-y"),
pm.newAutoConfirmCmd(pm.installVerb, caCertsPkgName),
pm.newAutoConfirmCmd(pm.deleteVerb, caCertsPkgName),
pm.newAutoConfirmCmd(pm.updateVerb, caCertsPkgName),
)
}

func (pm *DistroPackageManager) yumUtilsPackage() artifact.Package {
return artifact.NewPackageSource(
artifact.NewCmd(pm.manager, pm.installVerb, yumUtilsManagerPkg, "-y"),
artifact.NewCmd(pm.manager, pm.deleteVerb, yumUtilsManagerPkg, "-y"),
artifact.NewCmd(pm.manager, pm.updateVerb, yumUtilsManagerPkg, "-y"),
pm.newAutoConfirmCmd(pm.installVerb, yumUtilsManagerPkg),
pm.newAutoConfirmCmd(pm.deleteVerb, yumUtilsManagerPkg),
pm.newAutoConfirmCmd(pm.updateVerb, yumUtilsManagerPkg),
)
}

func (pm *DistroPackageManager) runcPackage() artifact.Package {
return artifact.NewPackageSource(
artifact.NewCmd(pm.manager, pm.installVerb, runcPkgName, "-y"),
artifact.NewCmd(pm.manager, pm.deleteVerb, runcPkgName, "-y"),
artifact.NewCmd(pm.manager, pm.updateVerb, runcPkgName, "-y"),
pm.newAutoConfirmCmd(pm.installVerb, runcPkgName),
pm.newAutoConfirmCmd(pm.deleteVerb, runcPkgName),
pm.newAutoConfirmCmd(pm.updateVerb, runcPkgName),
)
}

Expand All @@ -300,7 +327,7 @@ func (pm *DistroPackageManager) Cleanup() error {
}

func getOsPackageManager() (string, error) {
supportedManagers := []string{yumPackageManager, aptPackageManager}
supportedManagers := []string{yumPackageManager, aptPackageManager, zypperPackageManager}
for _, manager := range supportedManagers {
if _, err := exec.LookPath(manager); err == nil {
return manager, nil
Expand All @@ -310,25 +337,31 @@ func getOsPackageManager() (string, error) {
}

var packageManagerInstallCmd = map[string]string{
aptPackageManager: "install",
yumPackageManager: "install",
aptPackageManager: "install",
yumPackageManager: "install",
zypperPackageManager: "install",
}

var packageManagerUpdateCmd = map[string]string{
aptPackageManager: "upgrade",
yumPackageManager: "update",
aptPackageManager: "upgrade",
yumPackageManager: "update",
zypperPackageManager: "update",
}

var packageManagerDeleteCmd = map[string]string{
aptPackageManager: "autoremove",
yumPackageManager: "remove",
aptPackageManager: "autoremove",
yumPackageManager: "remove",
zypperPackageManager: "remove",
}

var packageManagerMetadataRefreshCmd = map[string]string{
aptPackageManager: "update",
yumPackageManager: "makecache",
aptPackageManager: "update",
yumPackageManager: "makecache",
zypperPackageManager: "refresh",
}

// managerToDockerRepoMap intentionally has no zypper entry: Docker does not
// publish an official docker-ce repo for SLES/openSUSE
var managerToDockerRepoMap = map[string]string{
yumPackageManager: "https://download.docker.com/linux/centos/docker-ce.repo",
aptPackageManager: "https://download.docker.com/linux/ubuntu",
Expand Down
1 change: 1 addition & 0 deletions internal/ssm/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ func setDaemonName() {
system.UbuntuOsName: "snap.amazon-ssm-agent.amazon-ssm-agent",
system.RhelOsName: "amazon-ssm-agent",
system.AmazonOsName: "amazon-ssm-agent",
system.SlesOsName: "amazon-ssm-agent",
}
osName := system.GetOsName()
if daemonName, ok := osToDaemonName[osName]; ok {
Expand Down
7 changes: 4 additions & 3 deletions internal/ssm/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,10 @@ func (s ssmInstallerSource) defaultBuildSSMURL() (string, error) {
// package manager in use.
func detectPlatformVariant() (string, error) {
toVariant := map[string]string{
"apt": "debian",
"dnf": "linux",
"yum": "linux",
"apt": "debian",
"dnf": "linux",
"yum": "linux",
"zypper": "linux",
}

for pkgManager := range toVariant {
Expand Down
1 change: 1 addition & 0 deletions internal/system/os.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const (
UbuntuOsName = "ubuntu"
RhelOsName = "rhel"
AmazonOsName = "amzn"
SlesOsName = "sles"

UbuntuResolvConfPath = "/run/systemd/resolve/resolv.conf"
)
Expand Down
Loading