Skip to content

Commit

Permalink
fix path lookup (#298)
Browse files Browse the repository at this point in the history
* fix path lookup
* preflight flag `--install_version` for overriding nex version assets installed during preflight

---------

Signed-off-by: Jordan Rash <[email protected]>
  • Loading branch information
jordan-rash authored Jul 1, 2024
1 parent c9aa165 commit 674ccbb
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 64 deletions.
9 changes: 5 additions & 4 deletions internal/models/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,11 @@ type NodeOptions struct {
OtelTraces bool `json:"-"`
OtelTracesExporter string `json:"-"`

PreflightInit string `json:"-"`
PreflightVerify bool `json:"-"`
PreflightVerbose bool `json:"-"`
PreflightCheck bool `json:"-"`
PreflightInit string `json:"-"`
PreflightVerify bool `json:"-"`
PreflightVerbose bool `json:"-"`
PreflightCheck bool `json:"-"`
PreflightInstallVersion string `json:"-"`

ListFull bool `json:"-"`
NexusName string `json:"-"`
Expand Down
7 changes: 4 additions & 3 deletions internal/models/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ type NodeConfiguration struct {
DenyTriggerSubjects []string `json:"deny_trigger_subjects,omitempty"`
NodeLimits NodeLimitsConfig `json:"node_limits"`

PreflightVerbose bool `json:"-"`
PreflightVerify bool `json:"-"`
PreflightCheck bool `json:"-"`
PreflightVerbose bool `json:"-"`
PreflightVerify bool `json:"-"`
PreflightCheck bool `json:"-"`
PreflightInstallVersion string `json:"-"`

// Public NATS server options; when non-nil, a public "userland" NATS server is started during node init
PublicNATSServer *server.Options `json:"public_nats_server,omitempty"`
Expand Down
1 change: 1 addition & 0 deletions internal/node/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func CmdPreflight(opts *nexmodels.Options, nodeopts *nexmodels.NodeOptions, ctx
config.PreflightCheck = nodeopts.PreflightCheck
config.PreflightVerify = nodeopts.PreflightVerify
config.PreflightVerbose = nodeopts.PreflightVerbose
config.PreflightInstallVersion = nodeopts.PreflightInstallVersion

err = preflight.Preflight(ctx, config, log)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions internal/node/preflight/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ var (
fmt.Printf("error making http request: %s\n", err)
return ""
}
if res.StatusCode != 200 {
fmt.Printf("error fetching latest version from github: %s\n", res.Status)
return ""
}
defer res.Body.Close()

b, err := io.ReadAll(res.Body)
Expand Down
12 changes: 11 additions & 1 deletion internal/node/preflight/preflight.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,17 @@ type requirement struct {
}

func Preflight(ctx context.Context, config *models.NodeConfiguration, logger *slog.Logger) PreflightError {
nexVer := nexLatestVersion(ctx)
var nexVer string
if config.PreflightInstallVersion == "" {
nexVer = nexLatestVersion(ctx)
if nexVer == "" {
logger.Error("Failed to get latest nex version")
return ErrFailedToDetermineLatestNexVersion
}
} else {
nexVer = config.PreflightInstallVersion
}

logger.Debug("using nex version", slog.String("version", nexVer))

required, err := preflightInit(nexVer, config, logger)
Expand Down
133 changes: 79 additions & 54 deletions internal/node/preflight/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,80 +12,105 @@ import (

type BinaryVerify struct {
BinName string
Error PreflightError
Path []string
}

type PreflightError error

var (
ErrNexAgentNotFound = errors.New("nex-agent binary not found")
ErrFirecrackerNotFound = errors.New("firecracker binary not found")
ErrHostLocalPluginNotFound = errors.New("host-local binary not found")
ErrPtpPluginNotFound = errors.New("ptp binary not found")
ErrTcRedirectTapPluginNotFound = errors.New("tc-redirect-tap binary not found")
ErrRootFsNotFound = errors.New("rootfs file not found")
ErrVmlinuxNotFound = errors.New("vmlinux file not found")
ErrCNIConfigNotFound = errors.New("cni config file not found")

ErrNoSandboxRequired = errors.New("no sandbox required for os")
ErrFailedToSatifyDependency = errors.New("failed to satisfy dependency")
ErrFailedToDownload = errors.New("failed to download file")
ErrFailedToDownloadKnownGoodSha = errors.New("failed to download known good shasum")
ErrFailedToCreateFile = errors.New("failed to create new binary file")
ErrFailedToWriteTempFile = errors.New("failed to write temp binary file")
ErrFailedToCalculateSha256 = errors.New("failed to calculate sha256")
ErrSha256Mismatch = errors.New("sha256 mismatch")
ErrFailedToUncompress = errors.New("failed to uncompress file")
ErrUserCanceledPreflight = errors.New("user canceled preflight")

binVerify = []BinaryVerify{
{BinName: "firecracker", Error: ErrFirecrackerNotFound},
{BinName: "host-local", Error: ErrHostLocalPluginNotFound},
{BinName: "ptp", Error: ErrPtpPluginNotFound},
{BinName: "tc-redirect-tap", Error: ErrTcRedirectTapPluginNotFound},
}
ErrBinaryNotFound = errors.New("binary not found")
ErrBinaryNotExecutable = errors.New("binary not executable")
ErrRootFsNotFound = errors.New("rootfs file not found")
ErrVmlinuxNotFound = errors.New("vmlinux file not found")
ErrCNIConfigNotFound = errors.New("cni config file not found")

ErrNoSandboxRequired = errors.New("no sandbox required for os")
ErrFailedToDetermineLatestNexVersion = errors.New("failed to determine latest nex version")
ErrFailedToSatifyDependency = errors.New("failed to satisfy dependency")
ErrFailedToDownload = errors.New("failed to download file")
ErrFailedToDownloadKnownGoodSha = errors.New("failed to download known good shasum")
ErrFailedToCreateFile = errors.New("failed to create new binary file")
ErrFailedToWriteTempFile = errors.New("failed to write temp binary file")
ErrFailedToCalculateSha256 = errors.New("failed to calculate sha256")
ErrSha256Mismatch = errors.New("sha256 mismatch")
ErrFailedToUncompress = errors.New("failed to uncompress file")
ErrUserCanceledPreflight = errors.New("user canceled preflight")
)

func Validate(config *models.NodeConfiguration, logger *slog.Logger) PreflightError {
var errs PreflightError
if config.NoSandbox {
nexAgentPath, err := exec.LookPath("nex-agent")
func verifyPath(binary string, path []string, logger *slog.Logger) (string, PreflightError) {
if len(path) == 0 {
e, err := exec.LookPath(binary)
if err != nil {
errs = errors.Join(errs, ErrNexAgentNotFound)
} else {
logger.Debug("nex-agent binary found", slog.String("path", nexAgentPath))
logger.Debug(binary + " binary NOT found")
return "", ErrBinaryNotFound
}
return errs
return e, nil
}

for _, bin := range binVerify {
binPath, err := exec.LookPath(bin.BinName)
for _, p := range path {
e := filepath.Join(p, binary)
f, err := os.Stat(e)
if err != nil {
errs = errors.Join(errs, bin.Error)
} else {
logger.Debug(bin.BinName+" binary found", slog.String("path", binPath))
continue
}

// if f.Mode()&0100 == 0 {
// logger.Debug(binary+" binary NOT executable", slog.String("path", e))
// return "", ErrBinaryNotExecutable
// } else {
logger.Debug(binary+" binary found", slog.String("path", e), slog.Any("perms", f.Mode()))
return e, nil
// }
}

_, err := os.Stat(config.RootFsFilepath)
if errors.Is(err, os.ErrNotExist) {
errs = errors.Join(errs, ErrRootFsNotFound)
logger.Debug(binary + " binary NOT found")
return "", ErrBinaryNotFound
}

func Validate(config *models.NodeConfiguration, logger *slog.Logger) PreflightError {
var errs PreflightError
var binVerify []BinaryVerify
if config.NoSandbox {
binVerify = []BinaryVerify{
{BinName: "nex-agent", Path: config.BinPath},
}
} else {
logger.Debug("rootfs file found", slog.String("path", config.RootFsFilepath))
binVerify = []BinaryVerify{
{BinName: "firecracker", Path: config.BinPath},
{BinName: "host-local", Path: config.CNI.BinPath},
{BinName: "ptp", Path: config.CNI.BinPath},
{BinName: "tc-redirect-tap", Path: config.CNI.BinPath},
}
}

_, err = os.Stat(config.KernelFilepath)
if errors.Is(err, os.ErrNotExist) {
errs = errors.Join(errs, ErrVmlinuxNotFound)
} else {
logger.Debug("vmlinux file found", slog.String("path", config.KernelFilepath))
for _, bin := range binVerify {
_, err := verifyPath(bin.BinName, bin.Path, logger)
if err != nil {
errs = errors.Join(errs, err)
}
}

_, err = os.Stat(filepath.Join("/etc/cni/conf.d", *config.CNI.NetworkName+".conflist"))
if errors.Is(err, os.ErrNotExist) {
errs = errors.Join(errs, ErrCNIConfigNotFound)
} else {
logger.Debug("cni config file found", slog.String("path", "/etc/cni/conf.d/"+*config.CNI.NetworkName+".conflist"))
if !config.NoSandbox {
_, err := os.Stat(config.RootFsFilepath)
if errors.Is(err, os.ErrNotExist) {
errs = errors.Join(errs, ErrRootFsNotFound)
} else {
logger.Debug("rootfs file found", slog.String("path", config.RootFsFilepath))
}

_, err = os.Stat(config.KernelFilepath)
if errors.Is(err, os.ErrNotExist) {
errs = errors.Join(errs, ErrVmlinuxNotFound)
} else {
logger.Debug("vmlinux file found", slog.String("path", config.KernelFilepath))
}

_, err = os.Stat(filepath.Join("/etc/cni/conf.d", *config.CNI.NetworkName+".conflist"))
if errors.Is(err, os.ErrNotExist) {
errs = errors.Join(errs, ErrCNIConfigNotFound)
} else {
logger.Debug("cni config file found", slog.String("path", "/etc/cni/conf.d/"+*config.CNI.NetworkName+".conflist"))
}
}

if errs != nil {
Expand Down
1 change: 1 addition & 0 deletions nex/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func setConditionalCommands() {
nodePreflight.Flag("verify", "does binary verification on download").Default("false").BoolVar(&NodeOpts.PreflightVerify)
nodePreflight.Flag("verbose", "prints additional information during install").Default("false").BoolVar(&NodeOpts.PreflightVerbose)
nodePreflight.Flag("check", "checks status of requirements without attempting to install").Default("false").BoolVar(&NodeOpts.PreflightCheck)
nodePreflight.Flag("install_version", "uses specific version of nex during preflight installs").PlaceHolder("0.3.0").StringVar(&NodeOpts.PreflightInstallVersion)
nodePreflight.Flag("cni_ns", "nameservers to use in CNI configuration file").StringsVar(&NodeOpts.CniNS)
}

Expand Down
4 changes: 3 additions & 1 deletion spec/node_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ var _ = Describe("nex node", func() {
opts = &models.Options{
Servers: _fixtures.natsServer.ClientURL(),
}
nodeOpts = &models.NodeOptions{}
nodeOpts = &models.NodeOptions{
PreflightInstallVersion: "0.2.5",
}

_ = os.MkdirAll(defaultCNIPluginBinPath, 0755)
_ = os.MkdirAll(defaultCNIConfigurationPath, 0755)
Expand Down
4 changes: 3 additions & 1 deletion spec/node_nofc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ var _ = Describe("nex node", func() {
opts = &models.Options{
Servers: _fixtures.natsServer.ClientURL(),
}
nodeOpts = &models.NodeOptions{}
nodeOpts = &models.NodeOptions{
PreflightInstallVersion: "0.2.5",
}

validResourceDirOnce.Do(func() {
validResourceDir = filepath.Join(os.TempDir(), fmt.Sprintf("%d-spec-nex-wd", _fixtures.seededRand.Int()))
Expand Down

0 comments on commit 674ccbb

Please sign in to comment.