Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Warnings that k8s service may not work #657

Merged
merged 11 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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
89 changes: 89 additions & 0 deletions src/k8s/cmd/k8s/hooks.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
package k8s

import (
"fmt"
"os"
"strings"
"syscall"

cmdutil "github.com/canonical/k8s/cmd/util"

bschimke95 marked this conversation as resolved.
Show resolved Hide resolved
"github.com/spf13/cobra"
)

// initialProcessEnvironmentVariables environment variables of initial process
const initialProcessEnvironmentVariables = "/proc/1/environ"
maci3jka marked this conversation as resolved.
Show resolved Hide resolved

// pathsOwnershipCheck paths to validate root is the ownership
maci3jka marked this conversation as resolved.
Show resolved Hide resolved
var pathsOwnershipCheck = []string{"/sys", "/proc", "/dev/kmsg"}

func chainPreRunHooks(hooks ...func(*cobra.Command, []string)) func(*cobra.Command, []string) {
return func(cmd *cobra.Command, args []string) {
for _, hook := range hooks {
Expand Down Expand Up @@ -34,3 +46,80 @@ func hookInitializeFormatter(env cmdutil.ExecutionEnvironment, format *string) f
}
}
}

// hookCheckLXD checks ownership of directories required for k8s to run.
// If potential issue found pops up warning for user.
maci3jka marked this conversation as resolved.
Show resolved Hide resolved
func hookCheckLXD() func(*cobra.Command, []string) {
return func(cmd *cobra.Command, args []string) {
if lxd, err := inLXDContainer(); lxd {
maci3jka marked this conversation as resolved.
Show resolved Hide resolved
var errMsgs []string
for _, pathToCheck := range pathsOwnershipCheck {
if err2 := validateRootOwnership(pathToCheck); err2 != nil {
errMsgs = append(errMsgs, err2.Error())
}
}
if len(errMsgs) > 0 {
if debug, _ := cmd.Flags().GetBool("debug"); debug {
bschimke95 marked this conversation as resolved.
Show resolved Hide resolved
cmd.PrintErrln("Warning: When validating required resources potential issues found:")
for _, errMsg := range errMsgs {
cmd.PrintErrln("\t", errMsg)
}
}
cmd.PrintErrln("The lxc profile for Canonical Kubernetes might be missing.")
cmd.PrintErrln("For running k8s inside LXD container refer to " +
"https://documentation.ubuntu.com/canonical-kubernetes/latest/snap/howto/install/lxd/")
}
} else if err != nil {
cmd.PrintErrf(err.Error())
}
}
}

// getOwnership given path of file returns UID, GID and error.
maci3jka marked this conversation as resolved.
Show resolved Hide resolved
func getOwnership(path string) (int, int, error) {
info, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return 0, 0, fmt.Errorf("%s do not exist", path)
} else {
return 0, 0, err
maci3jka marked this conversation as resolved.
Show resolved Hide resolved
}
}
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
return int(stat.Uid), int(stat.Gid), nil
} else {
return 0, 0, fmt.Errorf("cannot access %s", path)
}
}

// validateRootOwnership checks if given path owner root and root group.
func validateRootOwnership(path string) error {
UID, GID, err := getOwnership(path)
if err != nil {
return err
}
if UID != 0 {
return fmt.Errorf("owner of %s is user with UID %d expected 0", path, UID)
}
if GID != 0 {
return fmt.Errorf("owner of %s is group with GID %d expected 0", path, GID)
}
return nil
}

// inLXDContainer checks if k8s runs in lxd container.
func inLXDContainer() (bool, error) {
content, err := os.ReadFile(initialProcessEnvironmentVariables)
if err != nil {
// if permission to file is missing we still want to display info about lxd
bschimke95 marked this conversation as resolved.
Show resolved Hide resolved
if os.IsPermission(err) {
return true, err
maci3jka marked this conversation as resolved.
Show resolved Hide resolved
}
return false, err
}
env := string(content)
if strings.Contains(env, "container=lxc") {
return true, nil
}
return false, nil
}
2 changes: 1 addition & 1 deletion src/k8s/cmd/k8s/k8s_bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func newBootstrapCmd(env cmdutil.ExecutionEnvironment) *cobra.Command {
Use: "bootstrap",
Short: "Bootstrap a new Kubernetes cluster",
Long: "Generate certificates, configure service arguments and start the Kubernetes services.",
PreRun: chainPreRunHooks(hookRequireRoot(env), hookInitializeFormatter(env, &opts.outputFormat)),
PreRun: chainPreRunHooks(hookRequireRoot(env), hookInitializeFormatter(env, &opts.outputFormat), hookCheckLXD()),
Run: func(cmd *cobra.Command, args []string) {
if opts.interactive && opts.configFile != "" {
cmd.PrintErrln("Error: --interactive and --file flags cannot be set at the same time.")
Expand Down
2 changes: 1 addition & 1 deletion src/k8s/cmd/k8s/k8s_join_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func newJoinClusterCmd(env cmdutil.ExecutionEnvironment) *cobra.Command {
cmd := &cobra.Command{
Use: "join-cluster <join-token>",
Short: "Join a cluster using the provided token",
PreRun: chainPreRunHooks(hookRequireRoot(env), hookInitializeFormatter(env, &opts.outputFormat)),
PreRun: chainPreRunHooks(hookRequireRoot(env), hookInitializeFormatter(env, &opts.outputFormat), hookCheckLXD()),
Args: cmdutil.ExactArgs(env, 1),
Run: func(cmd *cobra.Command, args []string) {
token := args[0]
Expand Down
Loading