func ensureSCCExists(ns string, serviceAccountName string) error { binary, err := exec.LookPath("oc") if err != nil { // no openshift so ignore return nil } text, err := getCommandOutputString(binary, []string{"export", "scc", serviceAccountName}, os.Stdin) if err != nil { log.Debug("Failed to get SecurityContextConstraints %s. %s", serviceAccountName, err) } if err != nil || len(text) == 0 { text = ` apiVersion: v1 kind: SecurityContextConstraints groups: - system:cluster-admins - system:nodes metadata: creationTimestamp: null name: ` + serviceAccountName + ` runAsUser: type: RunAsAny seLinuxContext: type: RunAsAny supplementalGroups: type: RunAsAny users: ` } // lets ensure there's a users section if !strings.Contains(text, "\nusers:") { text = text + "\nusers:\n" } line := "system:serviceaccount:" + ns + ":" + serviceAccountName if strings.Contains(text, line) { log.Info("No need to modify SecurityContextConstraints as it already contains line for namespace %s and service account %s", ns, serviceAccountName) return nil } text = text + "\n- " + line + "\n" log.Debug("created SecurityContextConstraints YAML: %s", text) log.Info("Applying changes for SecurityContextConstraints %s for namespace %s and ServiceAccount %s", serviceAccountName, ns, serviceAccountName) reader := bytes.NewReader([]byte(text)) err = runCommand(binary, []string{"apply", "-f", "-"}, reader) if err != nil { log.Err("Failed to update OpenShift SecurityContextConstraints named %s. %s", serviceAccountName, err) } return err }
func forwardPort(conn net.Conn, address string) { client, err := net.Dial("tcp", address) if err != nil { log.Err("Dial failed: %v", err) } log.Info("Connected to localhost %v\n", conn) go func() { defer client.Close() defer conn.Close() io.Copy(client, conn) }() go func() { defer client.Close() defer conn.Close() io.Copy(conn, client) }() }
func forwardPortLoop(name string, address string, forwardAddress string) error { log.Info("forwarding port %s %s => %s", name, address, forwardAddress) listener, err := net.Listen("tcp", address) if err != nil { return err } log.Info("About to start the acceptor goroutine!") go func() { for { conn, err := listener.Accept() if err != nil { log.Err("Failed to accept listener: %v", err) } log.Info("Accepted connection %v\n", conn) go forwardPort(conn, forwardAddress) } }() return nil }
// Run runs a remote command on a given host to test out SSH / WinRM func Run(c *cli.Context) { port, err := osExpandAndVerifyGlobal(c, "port") if err != nil { fail(err) } command, err := osExpandAndVerify(c, "command") if err != nil { fail(err) } host, err := osExpandAndVerify(c, "host") if err != nil { fail(err) } user, err := osExpandAndVerify(c, "user") if err != nil { fail(err) } connection := c.String("connection") if connection == ansible.ConnectionWinRM { password, err := osExpandAndVerify(c, "password") if err != nil { fail(err) } err = winrm.RemoteWinRmCommand(user, password, host, port, command) } else { privatekey, err := osExpandAndVerify(c, "privatekey") if err != nil { fail(err) } envVars := make(map[string]string) err = ssh.RemoteSSHCommand(user, privatekey, host, port, command, envVars) } if err != nil { log.Err("Failed: %v", err) } }
} host = os.ExpandEnv(host) if host == "" { log.Die("Host is required") } user = os.ExpandEnv(user) if user == "" { log.Die("User is required") } if connection == ansible.ConnectionWinRM { password = os.ExpandEnv(password) if password == "" { log.Die("Password is required") } err := winrm.RemoteWinRmCommand(user, password, host, strconv.Itoa(sshPort), command, nil, nil, "") if err != nil { log.Err("Failed: %v", err) } } else { privatekey = os.ExpandEnv(privatekey) if privatekey == "" { log.Die("Private key is required") } err := ssh.RemoteSSHCommand(user, privatekey, host, strconv.Itoa(sshPort), command, nil) if err != nil { log.Err("Failed: %v", err) } } }, }
if len(command) == 0 { command = os.Getenv(ansible.EnvCommand) } if len(command) == 0 { plural := "" if len(commandEnvVars) > 1 { plural = "s" } log.Die("Could not find a command to execute from the environment variable%s: %s", plural, strings.Join(commandEnvVars, ", ")) } bash := os.ExpandEnv(bash) if len(bash) > 0 { err = generateBashScript(bash, connection) if err != nil { log.Err("Failed to generate bash script at %s due to: %v", bash, err) return } } if connection == ansible.ConnectionWinRM { password := hostEntry.Password if len(password) == 0 { password = os.ExpandEnv(passwordFlag) if password == "" { log.Die("Cannot connect without a password") } } err = winrm.RemoteWinRmCommand(user, password, host, port, command, kubeclient, rc, hostEntry.Name) } else { privatekey := hostEntry.PrivateKey
func main() { app := cli.NewApp() app.Name = "kansible" app.Usage = `Kansible Kansible orchestrates processes in the same way as you orchestrate Docker containers with Kubernetes. Once you have created an Ansible playbook to install and configure your software you can use Kansible to create a Kubernetes Replication Controller to run, scale and manage the processes providing a universal view in Kubernetes of all your containers and processes along with common scaling, high availability, service discovery and load balancing. More help is here: https://github.com/fabric8io/kansible/blob/master/README.md` app.Version = version app.EnableBashCompletion = true app.After = func(c *cli.Context) error { if log.ErrorState { return errors.New("Exiting with errors") } return nil } app.Flags = []cli.Flag{ cli.StringFlag{ Name: "port", Value: "22", Usage: "The port for the remote SSH connection", }, cli.BoolFlag{ Name: "debug", Usage: "Enable verbose debugging output", }, } app.CommandNotFound = func(c *cli.Context, command string) { log.Err("No matching command '%s'", command) cli.ShowAppHelp(c) log.Die("") } app.Commands = []cli.Command{ { Name: "rc", Usage: "Creates or updates the kansible ReplicationController for some hosts in an Ansible inventory.", Description: `This commmand will analyse the hosts in an Ansible inventory and creates or updates the ReplicationController for the kansible pods.`, ArgsUsage: "[hosts] [command]", Action: cmds.RC, Flags: []cli.Flag{ cli.StringFlag{ Name: "inventory", Value: "inventory", Usage: "The location of your Ansible inventory file", }, cli.StringFlag{ Name: "rc", Value: "rc.yml", Usage: "The YAML file of the ReplicationController for the supervisors", }, }, }, { Name: "pod", Usage: "Runs the kansible pod which owns a host from the Ansible inventory then runs a remote command on the host.", Description: `This commmand will pick an available host from the Ansible inventory, then run a remote command on that host.`, ArgsUsage: "[hosts] [command]", Action: cmds.Pod, Flags: []cli.Flag{ cli.StringFlag{ Name: "inventory", Value: "inventory", Usage: "The location of your Ansible inventory file", }, cli.StringFlag{ Name: "rc", Value: "$KANSIBLE_RC", Usage: "The name of the ReplicationController for the supervisors", }, cli.StringFlag{ Name: "password", Value: "$KANSIBLE_PASSWORD", Usage: "The password used for WinRM connections", }, cli.StringFlag{ Name: "connection", Usage: "The Ansible connection type to use. Defaults to SSH unless 'winrm' is defined to use WinRM on Windows", }, cli.StringFlag{ Name: "bash", Value: "$KANSIBLE_BASH", Usage: "If specified a script is generated for running a bash like shell on the remote machine", }, }, }, { Name: "run", Usage: "Runs a remote command on a given host to test out SSH / WinRM", Description: `This commmand will begin running the supervisor on an avaiable host.`, ArgsUsage: "[string]", Action: cmds.Run, Flags: []cli.Flag{ cli.StringFlag{ Name: "user", Value: "$KANSIBLE_USER", Usage: "The user to use on the remote connection", }, cli.StringFlag{ Name: "privatekey", Value: "$KANSIBLE_PRIVATEKEY", Usage: "The private key used for SSH", }, cli.StringFlag{ Name: "host", Value: "$KANSIBLE_HOST", Usage: "The host for the remote connection", }, cli.StringFlag{ Name: "command", Value: "$KANSIBLE_COMMAND", Usage: "The remote command to invoke on the host", }, cli.StringFlag{ Name: "password", Usage: "The password if using WinRM to execute the command", }, cli.StringFlag{ Name: "connection", Usage: "The Ansible connection type to use. Defaults to SSH unless 'winrm' is defined to use WinRM on Windows", }, }, }, } app.Before = func(c *cli.Context) error { log.IsDebugging = c.Bool("debug") return nil } app.RunAndExitOnError() }
// Pod runs the kansible pod for a given group of hosts in an Ansible playbook // this grabs a specific host (using annotations on the RC) then runs a remote command // on that host binding stdin, stdout, stderr to the remote process func Pod(c *cli.Context) { args := c.Args() if len(args) < 1 { log.Die("Expected arguments [hosts] [command]") } hosts := os.ExpandEnv(args[0]) command := "" if len(args) > 1 { command = os.ExpandEnv(strings.Join(args[1:], " ")) } f := cmdutil.NewFactory(nil) if f == nil { log.Die("Failed to create Kubernetes client factory!") } kubeclient, _ := f.Client() if kubeclient == nil { log.Die("Failed to create Kubernetes client!") } ns, _, _ := f.DefaultNamespace() if len(ns) == 0 { ns = "default" } inventory, err := osExpandAndVerify(c, "inventory") if err != nil { fail(err) } rcName, err := osExpandAndVerify(c, "rc") if err != nil { fail(err) } envVars := make(map[string]string) hostEntry, err := ansible.ChooseHostAndPrivateKey(inventory, hosts, kubeclient, ns, rcName, envVars) if err != nil { fail(err) } host := hostEntry.Host user := hostEntry.User port := hostEntry.Port if len(port) == 0 { port, err = osExpandAndVerifyGlobal(c, "port") } if err != nil { fail(err) } connection := hostEntry.Connection if len(connection) == 0 { connection = osExpand(c, "connection") } runCommand := hostEntry.RunCommand if len(runCommand) != 0 { command = runCommand } commandEnvVars := []string{} if len(command) == 0 { if len(connection) > 0 { envVarName := ansible.EnvCommand + "_" + strings.ToUpper(connection) commandEnvVars = append(commandEnvVars, envVarName) command = os.Getenv(envVarName) } } commandEnvVars = append(commandEnvVars, ansible.EnvCommand) if len(command) == 0 { command = os.Getenv(ansible.EnvCommand) } if len(command) == 0 { plural := "" if len(commandEnvVars) > 1 { plural = "s" } fail(fmt.Errorf("Could not find a command to execute from the environment variable%s: %s", plural, strings.Join(commandEnvVars, ", "))) } log.Info("running command on a host from %s and command `%s`", hosts, command) bash := osExpand(c, "bash") if len(bash) > 0 { err = generateBashScript(bash, connection) if err != nil { log.Err("Failed to generate bash script at %s due to: %v", bash, err) } } log.Info("using connection %s", connection) if connection == ansible.ConnectionWinRM { log.Info("Using WinRM to connect to the hosts %s", hosts) password := hostEntry.Password if len(password) == 0 { password, err = osExpandAndVerify(c, "password") if err != nil { fail(err) } } err = winrm.RemoteWinRmCommand(user, password, host, port, command) } else { privatekey := hostEntry.PrivateKey err = ssh.RemoteSSHCommand(user, privatekey, host, port, command, envVars) } if err != nil { log.Err("Failed: %v", err) } }