Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,7 @@ jobs:
test:
- TestResetAndReinstallAirgap
- TestSingleNodeAirgapUpgrade
- TestSingleNodeAirgapUpgradeSelinux
- TestSingleNodeAirgapUpgradeConfigValues
- TestSingleNodeAirgapUpgradeCustomCIDR
- TestMultiNodeAirgapUpgrade
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@ jobs:
test:
- TestResetAndReinstallAirgap
- TestSingleNodeAirgapUpgrade
- TestSingleNodeAirgapUpgradeSelinux
- TestSingleNodeAirgapUpgradeConfigValues
- TestSingleNodeAirgapUpgradeCustomCIDR
- TestMultiNodeAirgapUpgrade
Expand Down
18 changes: 13 additions & 5 deletions e2e/cluster/cmx/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,15 @@ func copyScriptsToNode(node Node) error {
}

func getSSHEndpoint(nodeID string) (string, error) {
output, err := exec.Command("replicated", "vm", "ssh-endpoint", nodeID).CombinedOutput()
args := []string{
"vm",
"ssh-endpoint",
nodeID,
}
if user := os.Getenv("CMX_SSH_USERNAME"); user != "" {
args = append(args, "--username", user)
}
output, err := exec.Command("replicated", args...).CombinedOutput()
if err != nil {
return "", fmt.Errorf("%v: %s", err, string(output))
}
Expand Down Expand Up @@ -335,7 +343,7 @@ func runCommandOnNode(node Node, line []string, envs ...map[string]string) (stri
line = append([]string{fmt.Sprintf("%s=%s", k, v)}, line...)
}
}
line = append([]string{"sudo"}, line...)
line = append([]string{"sudo", "PATH=$PATH:/usr/local/bin"}, line...)

cmd := exec.Command("ssh", append(sshArgs(), node.sshEndpoint, strings.Join(line, " "))...)
var stdout, stderr bytes.Buffer
Expand Down Expand Up @@ -363,7 +371,7 @@ func (c *Cluster) SetupPlaywrightAndRunTest(testName string, args ...string) (st

func (c *Cluster) SetupPlaywright(envs ...map[string]string) error {
c.t.Logf("%s: bypassing kurl-proxy", time.Now().Format(time.RFC3339))
_, stderr, err := c.RunCommandOnNode(0, []string{"bypass-kurl-proxy.sh"}, envs...)
_, stderr, err := c.RunCommandOnNode(0, []string{"/usr/local/bin/bypass-kurl-proxy.sh"}, envs...)
Copy link
Member

Choose a reason for hiding this comment

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

is adding /usr/local/bin here necessary if it's being added to PATH above?

Copy link
Member Author

Choose a reason for hiding this comment

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

during writing these tests I found that any $PATH settings would get ignored when we came to actually execute the scripts. I spent days banging my head against the problem and determined that using the absolute path was just simpler than working out where the problem was.

Copy link
Member Author

Choose a reason for hiding this comment

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

I believe the $PATH variable being stripped might actually be because of selinux 😓

Copy link
Member Author

Choose a reason for hiding this comment

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

ok this does get passed on but only in one place... when running the ec binary. all the .sh script calls fail if called with a PATH lookup 🤔

if err != nil {
return fmt.Errorf("bypass kurl-proxy: %v: %s", err, string(stderr))
}
Expand Down Expand Up @@ -401,7 +409,7 @@ func (c *Cluster) generateSupportBundle(envs ...map[string]string) {
go func(i int, wg *sync.WaitGroup) {
defer wg.Done()
c.t.Logf("%s: generating host support bundle from node %d", time.Now().Format(time.RFC3339), i)
if stdout, stderr, err := c.RunCommandOnNode(i, []string{"collect-support-bundle-host.sh"}, envs...); err != nil {
if stdout, stderr, err := c.RunCommandOnNode(i, []string{"/usr/local/bin/collect-support-bundle-host.sh"}, envs...); err != nil {
c.t.Logf("stdout: %s", stdout)
c.t.Logf("stderr: %s", stderr)
c.t.Logf("fail to generate support bundle from node %d: %v", i, err)
Expand All @@ -419,7 +427,7 @@ func (c *Cluster) generateSupportBundle(envs ...map[string]string) {
}

c.t.Logf("%s: generating cluster support bundle from node %d", time.Now().Format(time.RFC3339), c.supportBundleNodeIndex)
if stdout, stderr, err := c.RunCommandOnNode(c.supportBundleNodeIndex, []string{"collect-support-bundle-cluster.sh"}, envs...); err != nil {
if stdout, stderr, err := c.RunCommandOnNode(c.supportBundleNodeIndex, []string{"/usr/local/bin/collect-support-bundle-cluster.sh"}, envs...); err != nil {
c.t.Logf("stdout: %s", stdout)
c.t.Logf("stderr: %s", stderr)
c.t.Logf("fail to generate cluster support bundle from node %d: %v", c.supportBundleNodeIndex, err)
Expand Down
89 changes: 89 additions & 0 deletions e2e/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,95 @@ func TestSingleNodeAirgapUpgrade(t *testing.T) {
t.Logf("%s: test complete", time.Now().Format(time.RFC3339))
}

func TestSingleNodeAirgapUpgradeSelinux(t *testing.T) {
t.Parallel()

RequireEnvVars(t, []string{"SHORT_SHA"})

tc := cmx.NewCluster(&cmx.ClusterInput{
T: t,
Nodes: 1,
Distribution: "almalinux",
Version: "8",
})
defer tc.Cleanup()

t.Logf("%s: downloading airgap files on node 0", time.Now().Format(time.RFC3339))
initialVersion := fmt.Sprintf("appver-%s-previous-k0s", os.Getenv("SHORT_SHA"))
runInParallel(t,
func(t *testing.T) error {
return downloadAirgapBundleOnNode(t, tc, 0, initialVersion, AirgapInstallBundlePath, AirgapLicenseID)
}, func(t *testing.T) error {
return downloadAirgapBundleOnNode(t, tc, 0, fmt.Sprintf("appver-%s-upgrade", os.Getenv("SHORT_SHA")), AirgapUpgradeBundlePath, AirgapLicenseID)
},
)

t.Logf("%s: airgapping cluster", time.Now().Format(time.RFC3339))
if err := tc.Airgap(); err != nil {
t.Fatalf("failed to airgap cluster: %v", err)
}

t.Logf("%s: creating /.autorelabel file for SELinux relabeling", time.Now().Format(time.RFC3339))
if stdout, stderr, err := tc.RunCommandOnNode(0, []string{"touch", "/.autorelabel"}); err != nil {
t.Fatalf("fail to create /.autorelabel file on node %s: %v: %s: %s", tc.Nodes[0], err, stdout, stderr)
}

t.Logf("%s: rebooting VM for SELinux relabeling", time.Now().Format(time.RFC3339))
if stdout, stderr, err := tc.RunCommandOnNode(0, []string{"reboot"}); err != nil {
t.Fatalf("fail to reboot node %s: %v: %s: %s", tc.Nodes[0], err, stdout, stderr)
}

t.Logf("%s: waiting for node to reboot", time.Now().Format(time.RFC3339))
tc.WaitForReboot()

t.Logf("%s: setting selinux to Enforcing mode", time.Now().Format(time.RFC3339))
if stdout, stderr, err := tc.RunCommandOnNode(0, []string{"setenforce 1"}); err != nil {
t.Fatalf("fail to set selinux to Enforcing mode %s: %v: %s: %s", tc.Nodes[0], err, stdout, stderr)
}

t.Logf("%s: preparing embedded cluster airgap files", time.Now().Format(time.RFC3339))
line := []string{"/usr/local/bin/airgap-prepare.sh"}
if stdout, stderr, err := tc.RunCommandOnNode(0, line); err != nil {
t.Fatalf("fail to prepare airgap files on node %s: %v: %s: %s", tc.Nodes[0], err, stdout, stderr)
}

installSingleNodeWithOptions(t, tc, installOptions{
isAirgap: true,
version: initialVersion,
localArtifactMirrorPort: "50001", // choose an alternate lam port
})

if stdout, stderr, err := tc.SetupPlaywrightAndRunTest("deploy-app"); err != nil {
t.Fatalf("fail to run playwright test deploy-app: %v: %s: %s", err, stdout, stderr)
}

t.Logf("%s: checking installation state after app deployment", time.Now().Format(time.RFC3339))
line = []string{"/usr/local/bin/check-airgap-installation-state.sh", initialVersion, k8sVersionPrevious()}
if _, _, err := tc.RunCommandOnNode(0, line); err != nil {
t.Fatalf("fail to check installation state: %v", err)
}

checkNodeJoinCommand(t, tc, 0)

t.Logf("%s: running airgap update", time.Now().Format(time.RFC3339))
line = []string{"/usr/local/bin/airgap-update.sh"}
if _, _, err := tc.RunCommandOnNode(0, line); err != nil {
t.Fatalf("fail to run airgap update: %v", err)
}

appUpgradeVersion := fmt.Sprintf("appver-%s-upgrade", os.Getenv("SHORT_SHA"))
testArgs := []string{appUpgradeVersion}

t.Logf("%s: upgrading cluster", time.Now().Format(time.RFC3339))
if stdout, stderr, err := tc.RunPlaywrightTest("deploy-upgrade", testArgs...); err != nil {
t.Fatalf("fail to run playwright test deploy-upgrade: %v: %s: %s", err, stdout, stderr)
}

checkPostUpgradeState(t, tc)

t.Logf("%s: test complete", time.Now().Format(time.RFC3339))
}

func TestSingleNodeAirgapUpgradeCustomCIDR(t *testing.T) {
t.Parallel()

Expand Down
18 changes: 9 additions & 9 deletions e2e/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ func installSingleNodeWithOptions(t *testing.T, tc cluster.Cluster, opts install
line := []string{}

if opts.isAirgap {
line = append(line, "single-node-airgap-install.sh")
line = append(line, "/usr/local/bin/single-node-airgap-install.sh")
} else {
line = append(line, "single-node-install.sh")
line = append(line, "/usr/local/bin/single-node-install.sh")
// the cli/ui option is currently only applicable for online installs
if opts.viaCLI {
line = append(line, "cli")
Expand Down Expand Up @@ -136,7 +136,7 @@ func checkInstallationState(t *testing.T, tc cluster.Cluster) {
}

func checkInstallationStateWithOptions(t *testing.T, tc cluster.Cluster, opts installationStateOptions) {
line := []string{"check-installation-state.sh"}
line := []string{"/usr/local/bin/check-installation-state.sh"}
if opts.version != "" {
line = append(line, opts.version)
} else {
Expand Down Expand Up @@ -233,23 +233,23 @@ func joinWorkerNodeWithOptions(t *testing.T, tc cluster.Cluster, node int, opts

func waitForNodes(t *testing.T, tc cluster.Cluster, nodes int, envs map[string]string, args ...string) {
t.Logf("%s: all nodes joined, waiting for them to be ready", time.Now().Format(time.RFC3339))
stdout, stderr, err := tc.RunCommandOnNode(0, append([]string{"wait-for-ready-nodes.sh", fmt.Sprintf("%d", nodes)}, args...), envs)
stdout, stderr, err := tc.RunCommandOnNode(0, append([]string{"/usr/local/bin/wait-for-ready-nodes.sh", fmt.Sprintf("%d", nodes)}, args...), envs)
if err != nil {
t.Fatalf("fail to wait for ready nodes: %v: %s: %s", err, stdout, stderr)
}
}

func checkWorkerProfile(t *testing.T, tc cluster.Cluster, node int) {
t.Logf("checking worker profile on node %d", node)
line := []string{"check-worker-profile.sh"}
line := []string{"/usr/local/bin/check-worker-profile.sh"}
if stdout, stderr, err := tc.RunCommandOnNode(node, line); err != nil {
t.Fatalf("fail to check worker profile on node %d: %v: %s: %s", node, err, stdout, stderr)
}
}

func checkNodeJoinCommand(t *testing.T, tc cluster.Cluster, node int) {
t.Logf("node join command generation on node %d", node)
line := []string{"check-node-join-command.sh"}
line := []string{"/usr/local/bin/check-node-join-command.sh"}
if stdout, stderr, err := tc.RunCommandOnNode(node, line); err != nil {
t.Fatalf("fail to check if node join command is generated successfully on node %d: %v: %s: %s", node, err, stdout, stderr)
}
Expand All @@ -261,7 +261,7 @@ func downloadECRelease(t *testing.T, tc cluster.Cluster, node int) {

func downloadECReleaseWithOptions(t *testing.T, tc cluster.Cluster, node int, opts downloadECReleaseOptions) {
t.Logf("%s: downloading embedded cluster release on node %d", time.Now().Format(time.RFC3339), node)
line := []string{"vandoor-prepare.sh"}
line := []string{"/usr/local/bin/vandoor-prepare.sh"}

if opts.version != "" {
line = append(line, opts.version)
Expand Down Expand Up @@ -292,7 +292,7 @@ func resetInstallationWithOptions(t *testing.T, tc cluster.Cluster, node int, op

func resetInstallationWithError(t *testing.T, tc cluster.Cluster, node int, opts resetInstallationOptions) (string, string, error) {
t.Logf("%s: resetting the installation on node %d", time.Now().Format(time.RFC3339), node)
line := []string{"reset-installation.sh"}
line := []string{"/usr/local/bin/reset-installation.sh"}
if opts.force {
line = append(line, "--force")
}
Expand All @@ -304,7 +304,7 @@ func checkPostUpgradeState(t *testing.T, tc cluster.Cluster) {
}

func checkPostUpgradeStateWithOptions(t *testing.T, tc cluster.Cluster, opts postUpgradeStateOptions) {
line := []string{"check-postupgrade-state.sh"}
line := []string{"/usr/local/bin/check-postupgrade-state.sh"}

if opts.k8sVersion != "" {
line = append(line, opts.k8sVersion)
Expand Down
10 changes: 10 additions & 0 deletions pkg-new/hostutils/initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ func (h *HostUtils) ConfigureHost(ctx context.Context, rc runtimeconfig.RuntimeC
}
}

h.logger.Debugf("configuring selinux fcontext")
if err := h.ConfigureSELinuxFcontext(rc); err != nil {
h.logger.Debugf("unable to configure selinux fcontext: %v", err)
}

h.logger.Debugf("restoring selinux context")
if err := h.RestoreSELinuxContext(rc); err != nil {
h.logger.Debugf("unable to restore selinux context: %v", err)
}

h.logger.Debugf("configuring sysctl")
if err := h.ConfigureSysctl(); err != nil {
h.logger.Debugf("unable to configure sysctl: %v", err)
Expand Down
10 changes: 10 additions & 0 deletions pkg-new/hostutils/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ type HostUtilsInterface interface {
CreateSystemdUnitFiles(ctx context.Context, logger logrus.FieldLogger, rc runtimeconfig.RuntimeConfig, isWorker bool) error
WriteLocalArtifactMirrorDropInFile(rc runtimeconfig.RuntimeConfig) error
AddInsecureRegistry(registry string) error
ConfigureSELinuxFcontext(rc runtimeconfig.RuntimeConfig) error
RestoreSELinuxContext(rc runtimeconfig.RuntimeConfig) error
}

// Convenience functions
Expand Down Expand Up @@ -72,3 +74,11 @@ func WriteLocalArtifactMirrorDropInFile(rc runtimeconfig.RuntimeConfig) error {
func AddInsecureRegistry(registry string) error {
return h.AddInsecureRegistry(registry)
}

func ConfigureSELinuxFcontext(rc runtimeconfig.RuntimeConfig) error {
return h.ConfigureSELinuxFcontext(rc)
}

func RestoreSELinuxContext(rc runtimeconfig.RuntimeConfig) error {
return h.RestoreSELinuxContext(rc)
}
12 changes: 12 additions & 0 deletions pkg-new/hostutils/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ type MockHostUtils struct {
mock.Mock
}

// ConfigureSELinuxFcontext implements HostUtilsInterface.
func (m *MockHostUtils) ConfigureSELinuxFcontext(rc runtimeconfig.RuntimeConfig) error {
args := m.Called(rc)
return args.Error(0)
}

// RestoreSELinuxContext implements HostUtilsInterface.
func (m *MockHostUtils) RestoreSELinuxContext(rc runtimeconfig.RuntimeConfig) error {
args := m.Called(rc)
return args.Error(0)
}

// ConfigureHost mocks the ConfigureHost method
func (m *MockHostUtils) ConfigureHost(ctx context.Context, rc runtimeconfig.RuntimeConfig, opts InitForInstallOptions) error {
args := m.Called(ctx, rc, opts)
Expand Down
50 changes: 50 additions & 0 deletions pkg-new/hostutils/selinux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package hostutils

import (
"os/exec"

"github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig"
)

func (h *HostUtils) ConfigureSELinuxFcontext(rc runtimeconfig.RuntimeConfig) error {
h.logger.Debugln("checking for semanage binary in $PATH")
if _, err := exec.LookPath("semanage"); err != nil {
h.logger.Debugln("semanage not found in $PATH")
return nil
}

h.logger.Debugf("setting selinux fcontext for embedded-cluster binary directory to bin_t")
args := []string{
"fcontext",
"-a",
"-s",
"system_u",
"-t",
"bin_t",
rc.EmbeddedClusterBinsSubDir() + "(/.*)?",
}
out, err := exec.Command("semanage", args...).CombinedOutput()
if err != nil {
h.logger.Debugf("unable to set contexts on binary directory: %v", err)
h.logger.Debugln(string(out))
}

return nil
}

func (h *HostUtils) RestoreSELinuxContext(rc runtimeconfig.RuntimeConfig) error {
h.logger.Debugln("checking for restorecon binary in $PATH")
if _, err := exec.LookPath("restorecon"); err != nil {
h.logger.Debugln("restorecon not found in $PATH")
return nil
}

h.logger.Debugf("relabeling embedded-cluster data directory with restorecon")
out, err := exec.Command("restorecon", "-RvF", rc.EmbeddedClusterHomeDirectory()).CombinedOutput()
if err != nil {
h.logger.Debugf("unable to run restorecon: %v", err)
h.logger.Debugln(string(out))
}

return nil
}
Loading