Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Aug 30, 2024. It is now read-only.
/coder-v1-cliPublic archive

Environment subcommands#89

Closed
cmoog wants to merge14 commits intourfavefromenvs-ls
Closed
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
14 commits
Select commitHold shift + click to select a range
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
19 changes: 11 additions & 8 deletionsci/integration/integration_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,22 +34,25 @@ func TestCoderCLI(t *testing.T) {
tcli.StderrEmpty(),
)

c.Run(ctx, "coder version").Assert(t,
c.Run(ctx, "coder--version").Assert(t,
tcli.StderrEmpty(),
tcli.Success(),
tcli.StdoutMatches("linux"),
)

c.Run(ctx, "coder help").Assert(t,
c.Run(ctx, "coder--help").Assert(t,
tcli.Success(),
tcli.StderrMatches("Commands:"),
tcli.StderrMatches("Usage: coder"),
tcli.StdoutEmpty(),
tcli.StdoutMatches("COMMANDS:"),
tcli.StdoutMatches("USAGE:"),
)

headlessLogin(ctx, t, c)

c.Run(ctx, "coder envs").Assert(t,
tcli.Error(),
)

c.Run(ctx, "coder envs ls").Assert(t,
tcli.Success(),
)

Expand All@@ -66,14 +69,14 @@ func TestCoderCLI(t *testing.T) {
)

var user entclient.User
c.Run(ctx, `coder users ls -o json | jq -c '.[] | select( .username == "charlie")'`).Assert(t,
c.Run(ctx, `coder users ls --output json | jq -c '.[] | select( .username == "charlie")'`).Assert(t,
tcli.Success(),
stdoutUnmarshalsJSON(&user),
)
assert.Equal(t, "user email is as expected", "charlie@coder.com", user.Email)
assert.Equal(t, "username is as expected", "Charlie", user.Name)

c.Run(ctx, "coder users ls -o human | grep charlie").Assert(t,
c.Run(ctx, "coder users ls --output human | grep charlie").Assert(t,
tcli.Success(),
tcli.StdoutMatches("charlie"),
)
Expand All@@ -82,7 +85,7 @@ func TestCoderCLI(t *testing.T) {
tcli.Success(),
)

c.Run(ctx, "coder envs").Assert(t,
c.Run(ctx, "coder envs ls").Assert(t,
tcli.Error(),
)
}
Expand Down
1 change: 0 additions & 1 deletionci/integration/secrets_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,7 +36,6 @@ func TestSecrets(t *testing.T) {

c.Run(ctx, "coder secrets create").Assert(t,
tcli.Error(),
tcli.StdoutEmpty(),
)

// this tests the "Value:" prompt fallback
Expand Down
209 changes: 106 additions & 103 deletionscmd/coder/configssh.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,162 +11,165 @@ import (
"strings"
"time"

"github.com/spf13/pflag"
"go.coder.com/cli"
"go.coder.com/flog"

"cdr.dev/coder-cli/internal/config"
"cdr.dev/coder-cli/internal/entclient"
)
"github.com/urfave/cli"

var (
privateKeyFilepath = filepath.Join(os.Getenv("HOME"), ".ssh", "coder_enterprise")
"go.coder.com/flog"
)

type configSSHCmd struct {
filepath string
remove bool

startToken, startMessage, endToken string
}

func (cmd *configSSHCmd) Spec() cli.CommandSpec {
return cli.CommandSpec{
Name: "config-ssh",
Usage: "",
Desc: "add your Coder Enterprise environments to ~/.ssh/config",
func makeConfigSSHCmd() cli.Command {
var (
configpath string
remove = false
)

return cli.Command{
Name: "config-ssh",
Usage: "Configure SSH to access Coder environments",
Description: "Inject the proper OpenSSH configuration into your local SSH config file.",
Action: configSSH(&configpath, &remove),
Flags: []cli.Flag{
cli.StringFlag{
Name: "filepath",
Usage: "overide the default path of your ssh config file",
Value: filepath.Join(os.Getenv("HOME"), ".ssh", "config"),
TakesFile: true,
Destination: &configpath,
},
cli.BoolFlag{
Name: "remove",
Usage: "remove the auto-generated Coder Enterprise ssh config",
Destination: &remove,
},
},
}
}

func (cmd *configSSHCmd) RegisterFlags(fl *pflag.FlagSet) {
fl.BoolVar(&cmd.remove, "remove", false, "remove the auto-generated Coder Enterprise ssh config")
home := os.Getenv("HOME")
defaultPath := filepath.Join(home, ".ssh", "config")
fl.StringVar(&cmd.filepath, "config-path", defaultPath, "overide the default path of your ssh config file")

cmd.startToken = "# ------------START-CODER-ENTERPRISE-----------"
cmd.startMessage = `# The following has been auto-generated by "coder config-ssh"
func configSSH(filepath *string, remove *bool) func(c *cli.Context) {
startToken := "# ------------START-CODER-ENTERPRISE-----------"
startMessage := `# The following has been auto-generated by "coder config-ssh"
# to make accessing your Coder Enterprise environments easier.
#
# To remove this blob, run:
#
# coder config-ssh --remove
#
# You should not hand-edit this section, unless you are deleting it.`
cmd.endToken = "# ------------END-CODER-ENTERPRISE------------"
}

func (cmd *configSSHCmd) Run(fl *pflag.FlagSet) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
endToken := "# ------------END-CODER-ENTERPRISE------------"

return func(c *cli.Context) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

currentConfig, err := readStr(*filepath)
if os.IsNotExist(err) {
// SSH configs are not always already there.
currentConfig = ""
} else if err != nil {
flog.Fatal("failed to read ssh config file %q: %v", filepath, err)
}

currentConfig, err := readStr(cmd.filepath)
if os.IsNotExist(err) {
// SSH configs are not always already there.
currentConfig = ""
} else if err != nil {
flog.Fatal("failed to read ssh config file %q: %v", cmd.filepath, err)
}
startIndex := strings.Index(currentConfig, startToken)
endIndex := strings.Index(currentConfig, endToken)

startIndex := strings.Index(currentConfig, cmd.startToken)
endIndex := strings.Index(currentConfig, cmd.endToken)
if *remove {
if startIndex == -1 || endIndex == -1 {
flog.Fatal("the Coder Enterprise ssh configuration section could not be safely deleted or does not exist")
}
currentConfig = currentConfig[:startIndex-1] + currentConfig[endIndex+len(endToken)+1:]

if cmd.remove {
if startIndex == -1 || endIndex == -1 {
flog.Fatal("the Coder Enterprise ssh configuration section could not be safely deleted or does not exist")
}
currentConfig = currentConfig[:startIndex-1] + currentConfig[endIndex+len(cmd.endToken)+1:]
err = writeStr(*filepath, currentConfig)
if err != nil {
flog.Fatal("failed to write to ssh config file %q: %v", *filepath, err)
}

err = writeStr(cmd.filepath, currentConfig)
if err != nil {
flog.Fatal("failed to write to ssh config file %q: %v", cmd.filepath, err)
return
}

return
}
entClient := requireAuth()

entClient := requireAuth()
sshAvailable := isSSHAvailable(ctx)
if !sshAvailable {
flog.Fatal("SSH is disabled or not available for your Coder Enterprise deployment.")
}

sshAvailable:=cmd.ensureSSHAvailable(ctx)
if!sshAvailable {
flog.Fatal("SSH is disabled or not available for your Coder Enterprise deployment.")
}
me, err:=entClient.Me()
iferr != nil {
flog.Fatal("failed to fetch username: %v", err)
}

me, err := entClient.Me()
if err != nil {
flog.Fatal("failed to fetch username: %v", err)
}
envs := getEnvs(entClient)
if len(envs) < 1 {
flog.Fatal("no environments found")
}
newConfig, err := makeNewConfigs(me.Username, envs, startToken, startMessage, endToken)
if err != nil {
flog.Fatal("failed to make new ssh configurations: %v", err)
}

envs := getEnvs(entClient)
if len(envs) < 1 {
flog.Fatal("no environments found")
}
newConfig, err := cmd.makeNewConfigs(me.Username, envs)
if err != nil {
flog.Fatal("failed to make new ssh configurations: %v", err)
}
// if we find the old config, remove those chars from the string
if startIndex != -1 && endIndex != -1 {
currentConfig = currentConfig[:startIndex-1] + currentConfig[endIndex+len(endToken)+1:]
}

// if we find the old config, remove those chars from the string
if startIndex != -1 && endIndex != -1 {
currentConfig = currentConfig[:startIndex-1] + currentConfig[endIndex+len(cmd.endToken)+1:]
}
err = writeStr(*filepath, currentConfig+newConfig)
if err != nil {
flog.Fatal("failed to write new configurations to ssh config file %q: %v", filepath, err)
}
err = writeSSHKey(ctx, entClient)
if err != nil {
flog.Fatal("failed to fetch and write ssh key: %v", err)
}

err = writeStr(cmd.filepath, currentConfig+newConfig)
if err != nil {
flog.Fatal("failed to write new configurations to ssh config file %q: %v", cmd.filepath, err)
}
err = writeSSHKey(ctx, entClient)
if err != nil {
flog.Fatal("failed to fetch and write ssh key: %v", err)
fmt.Printf("An auto-generated ssh config was written to %q\n", *filepath)
fmt.Printf("Your private ssh key was written to %q\n", privateKeyFilepath)
fmt.Println("You should now be able to ssh into your environment")
fmt.Printf("For example, try running\n\n\t$ ssh coder.%s\n\n", envs[0].Name)
}

fmt.Printf("An auto-generated ssh config was written to %q\n", cmd.filepath)
fmt.Printf("Your private ssh key was written to %q\n", privateKeyFilepath)
fmt.Println("You should now be able to ssh into your environment")
fmt.Printf("For example, try running\n\n\t$ ssh coder.%s\n\n", envs[0].Name)
}

var (
privateKeyFilepath = filepath.Join(os.Getenv("HOME"), ".ssh", "coder_enterprise")
)

func writeSSHKey(ctx context.Context, client *entclient.Client) error {
key, err := client.SSHKey()
if err != nil {
return err
}
err = ioutil.WriteFile(privateKeyFilepath, []byte(key.PrivateKey), 0400)
if err != nil {
return err
}
return nil
return ioutil.WriteFile(privateKeyFilepath, []byte(key.PrivateKey), 0400)
}

func(cmd *configSSHCmd)makeNewConfigs(userName string, envs []entclient.Environment) (string, error) {
func makeNewConfigs(userName string, envs []entclient.Environment, startToken, startMsg, endToken string) (string, error) {
hostname, err := configuredHostname()
if err != nil {
return "", nil
}

newConfig := fmt.Sprintf("\n%s\n%s\n\n",cmd.startToken,cmd.startMessage)
newConfig := fmt.Sprintf("\n%s\n%s\n\n", startToken,startMsg)
for _, env := range envs {
newConfig +=cmd.makeConfig(hostname, userName, env.Name)
newConfig +=makeSSHConfig(hostname, userName, env.Name)
}
newConfig += fmt.Sprintf("\n%s\n",cmd.endToken)
newConfig += fmt.Sprintf("\n%s\n", endToken)

return newConfig, nil
}

func(cmd *configSSHCmd) makeConfig(host, userName, envName string) string {
funcmakeSSHConfig(host, userName, envName string) string {
return fmt.Sprintf(
`Host coder.%s
HostName %s
User %s-%s
StrictHostKeyChecking no
ConnectTimeout=0
IdentityFile=%s
ServerAliveInterval 60
ServerAliveCountMax 3
HostName %s
User %s-%s
StrictHostKeyChecking no
ConnectTimeout=0
IdentityFile=%s
ServerAliveInterval 60
ServerAliveCountMax 3
`, envName, host, userName, envName, privateKeyFilepath)
}

func(cmd *configSSHCmd) ensureSSHAvailable(ctx context.Context) bool {
funcisSSHAvailable(ctx context.Context) bool {
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()

Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp