Skip to content

Commit

Permalink
Install cmd: add user setup + cobra cmd
Browse files Browse the repository at this point in the history
Signed-off-by: Karen Almog <kalmog@mirantis.com>
  • Loading branch information
Karen Almog committed Dec 1, 2020
1 parent c981751 commit 17156a8
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ bindata
.*.stamp
.tmp
.terraform
.idea
*.tfstate*
aws_private.pem
out.json
Expand Down
83 changes: 83 additions & 0 deletions cmd/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package cmd

import (
"fmt"
"os"
"reflect"
"strings"

"github.com/sirupsen/logrus"
"github.com/spf13/cobra"

"github.com/k0sproject/k0s/pkg/apis/v1beta1"
"github.com/k0sproject/k0s/pkg/install"
)

func init() {
installCmd.Flags().StringVar(&role, "role", "server", "node role (possible values: server or worker. In a single-node setup, a worker role should be used)")
}

var (
role string

installCmd = &cobra.Command{
Use: "install",
Short: "Helper command for setting up k0s on a brand-new system. Must be run as root (or with sudo)",
RunE: func(cmd *cobra.Command, args []string) error {
switch role {
case "server", "worker":
return setup()
default:
logrus.Errorf("invalid value %s for install role", role)
return cmd.Help()
}
},
}
)

// the setup functions:
// * Ensures that the proper users are created
// * sets up startup and logging for k0s
func setup() error {
if os.Geteuid() != 0 {
logrus.Fatal("this command must be run as root!")
}

if role == "server" {
if err := createServerUsers(); err != nil {
logrus.Errorf("failed to create server users: %v", err)
}
}

return nil
}

func createServerUsers() error {
clusterConfig, err := ConfigFromYaml(cfgFile)
if err != nil {
return err
}
users := getUserList(*clusterConfig.Install.SystemUsers)

var messages []string
for _, v := range users {
if err := install.EnsureUser(v, k0sVars.DataDir); err != nil {
messages = append(messages, err.Error())
}
}

if len(messages) > 0 {
return fmt.Errorf(strings.Join(messages, "\n"))
}
return nil
}

func getUserList(sysUsers v1beta1.SystemUser) []string {
v := reflect.ValueOf(sysUsers)
values := make([]string, v.NumField())

for i := 0; i < v.NumField(); i++ {
values[i] = v.Field(i).String()
}
return values
}
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func init() {
rootCmd.AddCommand(etcdCmd)
rootCmd.AddCommand(docs)
rootCmd.AddCommand(userCmd)
rootCmd.AddCommand(installCmd)

longDesc = "k0s - The zero friction Kubernetes - https://k0sproject.io"
if build.EulaNotice != "" {
Expand Down
2 changes: 1 addition & 1 deletion cmd/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ users:
Use: "token",
Short: "Manage join tokens",
RunE: func(cmd *cobra.Command, args []string) error {
return nil
return tokenCreateCmd.Usage()
},
}

Expand Down
5 changes: 3 additions & 2 deletions cmd/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@ users:

// userCmd creates new certs and kubeConfig for a user
userCmd = &cobra.Command{
Use: "user",
Use: "user [command]",
Short: "Manage user access",
RunE: func(cmd *cobra.Command, args []string) error {
return nil
// user command does nothing
return userCreateCmd.Usage()
},
}

Expand Down
15 changes: 13 additions & 2 deletions internal/util/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ package util
import (
"fmt"
"os"
"os/exec"
)

// FileExists checks if a file exists and is not a directory before we
// try using it to prevent further errors.
func FileExists(filename string) bool {
info, err := os.Stat(filename)
func FileExists(fileName string) bool {
info, err := os.Stat(fileName)
if os.IsNotExist(err) {
return false
}
Expand All @@ -42,3 +43,13 @@ func CheckPathPermissions(path string, perm os.FileMode) error {
}
return nil
}

// Find the path for a given file (similar to `which`)
func GetExecPath(fileName string) (*string, error) {
path, err := exec.LookPath(fileName)
if err != nil {
return nil, err
}

return &path, nil
}
23 changes: 23 additions & 0 deletions internal/util/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,26 @@ func GetGID(name string) (int, error) {
}
return strconv.Atoi(entry.Gid)
}

func CheckIfUserExists(name string) (bool, error) {
_, err := user.Lookup(name)
if _, ok := err.(user.UnknownUserError); ok {
return false, nil
}
if err != nil {
return false, err
}
return true, nil
}

/*
func GetLinuxDist() (string, error) {
if runtime.GOOS == "windows" {
return "", fmt.Errorf("unsupported OS")
}
cfg, err := ini.Load("/etc/os-release")
if err != nil {
fmt.Printf("failed to read file: %v", err)
}
return cfg.Section("").Key("ID").String(), nil
}*/
87 changes: 87 additions & 0 deletions pkg/install/users.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package install

import (
"fmt"
"os"
"os/exec"
"os/user"
"strings"

"github.com/sirupsen/logrus"

"github.com/k0sproject/k0s/internal/util"
)

// EnsureUser checks if a user exists, and creates it, if it doesn't
// TODO: we should also consider modifying the user, if the user exists, but with wrong settings
func EnsureUser(name string, homeDir string) error {
shell, err := util.GetExecPath("nologin")
if err != nil {
return err
}

exists, err := util.CheckIfUserExists(name)
// User doesn't exist
if !exists && err == nil {
// Create the User
if err := CreateUser(name, homeDir, *shell); err != nil {
return err
}
// User perhaps exists, but cannot be fetched
} else if err != nil {
return err
}
// verify that user can be fetched, and exists
_, err = user.Lookup(name)
if err != nil {
return err
}
return nil
}

// CreateUser creates a system user with either `adduser` or `useradd` command
func CreateUser(userName string, homeDir string, shell string) error {
var userCmd string
var userCmdArgs []string

logrus.Infof("creating user: %s", userName)
_, err := util.GetExecPath("useradd")
if err == nil {
userCmd = "useradd"
userCmdArgs = []string{`--home`, homeDir, `--shell`, shell, `--system`, userName}
} else {
userCmd = "adduser"
userCmdArgs = []string{`--disabled-password`, `--gecos`, `""`, `--home`, homeDir, `--shell`, shell, `--system`, userName}
}

cmd := exec.Command(userCmd, userCmdArgs...)
if err := execCmd(cmd); err != nil {
return err
}
return nil
}

// cmd wrapper
func execCmd(cmd *exec.Cmd) error {
logrus.Debugf("executing command: %v", quoteCmd(cmd))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to run command %s: %v", quoteCmd(cmd), err)
}
return nil
}

// parse a cmd struct to string
func quoteCmd(cmd *exec.Cmd) string {
if len(cmd.Args) == 0 {
return fmt.Sprintf("%q", cmd.Path)
}

var q []string
for _, s := range cmd.Args {
q = append(q, fmt.Sprintf("%q", s))
}
return strings.Join(q, ` `)
}

0 comments on commit 17156a8

Please sign in to comment.