diff --git a/pkg/cluster/manager/check.go b/pkg/cluster/manager/check.go index 0559aa8f90..99addd799e 100644 --- a/pkg/cluster/manager/check.go +++ b/pkg/cluster/manager/check.go @@ -686,7 +686,10 @@ func fixFailedChecks(host string, res *operator.CheckResult, t *task.Builder, sy case operator.CheckNameTHP: t.Shell(host, fmt.Sprintf( - `if [ -d %[1]s ]; then echo never > %[1]s/enabled; fi && %s`, + // grubby only exists on RHEL-family distros; skip the persistent + // kernel argument when it's not available (e.g. Debian/Ubuntu) + // instead of failing the whole apply. + `if [ -d %[1]s ]; then echo never > %[1]s/enabled; fi && if command -v grubby >/dev/null 2>&1; then %s; fi`, "/sys/kernel/mm/transparent_hugepage", `grubby --update-kernel=ALL --args="transparent_hugepage=never"`, ), diff --git a/pkg/cluster/operation/check.go b/pkg/cluster/operation/check.go index f0e69e111e..5928d0b410 100644 --- a/pkg/cluster/operation/check.go +++ b/pkg/cluster/operation/check.go @@ -608,9 +608,15 @@ func CheckSELinuxStatus(ctx context.Context, e ctxt.Executor, sudo bool) *CheckR Command: "getenforce", Sudo: sudo, }) - stdout, stderr, err := m.Execute(ctx, e) + stdout, _, err := m.Execute(ctx, e) if err != nil { - result.Err = fmt.Errorf("%w %s", err, stderr) + // getenforce is unavailable (e.g. SELinux userspace tools are not + // installed, as on most Debian/Ubuntu hosts), which means SELinux is + // not enforcing on this host. Treat it as disabled rather than a + // failure, so we don't trigger a fix that edits a non-existent + // /etc/selinux/config. The configuration is still checked separately + // by CheckSELinuxConf. + result.Msg = "getenforce not available, assuming SELinux is disabled" return result } out := strings.Trim(string(stdout), "\n") diff --git a/pkg/environment/env.go b/pkg/environment/env.go index 8ea388cc3e..c88150823a 100644 --- a/pkg/environment/env.go +++ b/pkg/environment/env.go @@ -14,6 +14,7 @@ package environment import ( + "errors" "fmt" "os" "path/filepath" @@ -21,7 +22,7 @@ import ( "strings" "time" - "github.com/pingcap/errors" + perrs "github.com/pingcap/errors" "github.com/pingcap/tiup/pkg/localdata" "github.com/pingcap/tiup/pkg/repository" "github.com/pingcap/tiup/pkg/repository/v1manifest" @@ -32,7 +33,7 @@ import ( var ( // ErrInstallFirst indicates that a component/version is not installed - ErrInstallFirst = errors.New("component not installed") + ErrInstallFirst = perrs.New("component not installed") ) // EnvList is the canonical allowlist of environment variables TiUP will print or expose. @@ -144,7 +145,22 @@ func InitEnv(options repository.Options, mOpt repository.MirrorOptions) (*Enviro var local v1manifest.LocalManifests local, err = v1manifest.NewManifests(profile) if err != nil { - return nil, errors.Annotatef(err, "initial repository from mirror(%s) failed", mirrorAddr) + if errors.Is(err, v1manifest.ErrLoadManifest) { + // Only bootstrap root.json when the file is actually missing. + // If it exists but can't be read (e.g. permissions), preserve the + // original error rather than overwriting it. + rootPath := profile.Path("bin", "root.json") + if _, statErr := os.Stat(rootPath); os.IsNotExist(statErr) { + // Use the configured mirrorAddr so that custom/test mirrors are respected. + if err := profile.ResetMirror(mirrorAddr, ""); err != nil { + return nil, perrs.Annotatef(err, "initial repository from mirror(%s) failed", mirrorAddr) + } + local, err = v1manifest.NewManifests(profile) + } + } + if err != nil { + return nil, perrs.Annotatef(err, "initial repository from mirror(%s) failed", mirrorAddr) + } } v1repo = repository.NewV1Repo(mirror, options, local) @@ -222,7 +238,7 @@ func (env *Environment) SelectInstalledVersion(component string, ver utils.Versi versions := []string{} for _, v := range installed { vi, err := env.v1Repo.LocalComponentVersion(component, v, true) - if errors.Cause(err) == repository.ErrUnknownVersion { + if perrs.Cause(err) == repository.ErrUnknownVersion { continue } if err != nil { @@ -238,9 +254,9 @@ func (env *Environment) SelectInstalledVersion(component string, ver utils.Versi return semver.Compare(versions[i], versions[j]) > 0 }) - errInstallFirst := errors.Annotatef(ErrInstallFirst, "use `tiup install %s` to install component `%s` first", component, component) + errInstallFirst := perrs.Annotatef(ErrInstallFirst, "use `tiup install %s` to install component `%s` first", component, component) if !ver.IsEmpty() { - errInstallFirst = errors.Annotatef(ErrInstallFirst, "use `tiup install %s:%s` to install specified version", component, ver.String()) + errInstallFirst = perrs.Annotatef(ErrInstallFirst, "use `tiup install %s:%s` to install specified version", component, ver.String()) } if ver.IsEmpty() || string(ver) == utils.NightlyVersionAlias { diff --git a/pkg/localdata/profile.go b/pkg/localdata/profile.go index 1712536fb5..0d29550c01 100644 --- a/pkg/localdata/profile.go +++ b/pkg/localdata/profile.go @@ -224,6 +224,11 @@ func (p *Profile) VersionIsInstalled(component, version string) (bool, error) { // ResetMirror reset root.json and cleanup manifests directory func (p *Profile) ResetMirror(addr, root string) error { + // Ensure bin directory exists + if err := os.MkdirAll(p.Path("bin"), 0755); err != nil { + return err + } + // Calculating root.json path shaWriter := sha256.New() if _, err := io.Copy(shaWriter, strings.NewReader(addr)); err != nil {