github.com/drycc/workflow-cli@v1.5.3-0.20240322092846-d4ee25983af9/cmd/keys.go (about)

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"path/filepath"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/drycc/controller-sdk-go/api"
    12  	"github.com/drycc/controller-sdk-go/keys"
    13  	"github.com/drycc/workflow-cli/pkg/ssh"
    14  	"github.com/drycc/workflow-cli/settings"
    15  )
    16  
    17  // KeysList lists a user's keys.
    18  func (d *DryccCmd) KeysList(results int) error {
    19  	s, err := settings.Load(d.ConfigFile)
    20  
    21  	if err != nil {
    22  		return err
    23  	}
    24  
    25  	if results == defaultLimit {
    26  		results = s.Limit
    27  	}
    28  
    29  	keys, _, err := keys.List(s.Client, results)
    30  	if d.checkAPICompatibility(s.Client, err) != nil {
    31  		return err
    32  	}
    33  
    34  	if len(keys) > 0 {
    35  		table := d.getDefaultFormatTable([]string{"ID", "OWNER", "KEY"})
    36  		for _, key := range keys {
    37  			table.Append([]string{
    38  				key.ID,
    39  				key.Owner,
    40  				fmt.Sprintf("%s...%s", key.Public[:16], key.Public[len(key.Public)-10:]),
    41  			})
    42  		}
    43  		table.Render()
    44  	} else {
    45  		d.Println("No any key found.")
    46  	}
    47  	return nil
    48  }
    49  
    50  // KeyRemove removes keys.
    51  func (d *DryccCmd) KeyRemove(keyID string) error {
    52  	s, err := settings.Load(d.ConfigFile)
    53  
    54  	if err != nil {
    55  		return err
    56  	}
    57  
    58  	d.Printf("Removing %s SSH Key...", keyID)
    59  
    60  	if err = keys.Delete(s.Client, keyID); d.checkAPICompatibility(s.Client, err) != nil {
    61  		d.Println()
    62  		return err
    63  	}
    64  
    65  	d.Println(" done")
    66  	return nil
    67  }
    68  
    69  // KeyAdd adds keys.
    70  func (d *DryccCmd) KeyAdd(name string, keyLocation string) error {
    71  	s, err := settings.Load(d.ConfigFile)
    72  
    73  	if err != nil {
    74  		return err
    75  	}
    76  
    77  	var key api.KeyCreateRequest
    78  
    79  	// check if name is the key
    80  	if name != "" && keyLocation == "" {
    81  		// detect of name is a file
    82  		_, err := os.Stat(name)
    83  		if err == nil {
    84  			keyLocation = name
    85  			name = ""
    86  		}
    87  	}
    88  
    89  	if keyLocation == "" {
    90  		ks, err := listKeys(d.WOut)
    91  		if err != nil {
    92  			return err
    93  		}
    94  		key, err = chooseKey(ks, d.WIn, d.WOut)
    95  		if err != nil {
    96  			return err
    97  		}
    98  	} else {
    99  		key, err = getKey(keyLocation)
   100  		if err != nil {
   101  			return err
   102  		}
   103  	}
   104  
   105  	// if name is provided by user then overwrite that in the key object
   106  	if name != "" {
   107  		key.ID = name
   108  	}
   109  
   110  	d.Printf("Uploading %s to drycc...", filepath.Base(key.Name))
   111  
   112  	if _, err = keys.New(s.Client, key.ID, key.Public); d.checkAPICompatibility(s.Client, err) != nil {
   113  		d.Println()
   114  		return err
   115  	}
   116  
   117  	d.Println(" done")
   118  	return nil
   119  }
   120  
   121  func chooseKey(keys []api.KeyCreateRequest, input io.Reader,
   122  	wOut io.Writer) (api.KeyCreateRequest, error) {
   123  	fmt.Fprintln(wOut, "Found the following SSH public keys:")
   124  
   125  	for i, key := range keys {
   126  		fmt.Fprintf(wOut, "%d) %s %s\n", i+1, filepath.Base(key.Name), key.ID)
   127  	}
   128  
   129  	fmt.Fprintln(wOut, "0) Enter path to pubfile (or use keys:add <key_path>)")
   130  
   131  	var selected string
   132  
   133  	fmt.Fprint(wOut, "Which would you like to use with Drycc? ")
   134  	fmt.Fscanln(input, &selected)
   135  
   136  	numSelected, err := strconv.Atoi(selected)
   137  
   138  	if err != nil {
   139  		return api.KeyCreateRequest{}, fmt.Errorf("%s is not a valid integer", selected)
   140  	}
   141  
   142  	if numSelected < 0 || numSelected > len(keys) {
   143  		return api.KeyCreateRequest{}, fmt.Errorf("%d is not a valid option", numSelected)
   144  	}
   145  
   146  	if numSelected == 0 {
   147  		var filename string
   148  
   149  		fmt.Fprint(wOut, "Enter the path to the pubkey file: ")
   150  		fmt.Fscanln(input, &filename)
   151  
   152  		return getKey(filename)
   153  	}
   154  
   155  	return keys[numSelected-1], nil
   156  }
   157  
   158  func listKeys(wOut io.Writer) ([]api.KeyCreateRequest, error) {
   159  	folder := filepath.Join(settings.FindHome(), ".ssh")
   160  	files, err := os.ReadDir(folder)
   161  
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	var keys []api.KeyCreateRequest
   167  
   168  	for _, file := range files {
   169  		if filepath.Ext(file.Name()) == ".pub" {
   170  			key, err := getKey(filepath.Join(folder, file.Name()))
   171  
   172  			if err == nil {
   173  				keys = append(keys, key)
   174  			} else {
   175  				fmt.Fprintln(wOut, err)
   176  			}
   177  		}
   178  	}
   179  
   180  	return keys, nil
   181  }
   182  
   183  func getKey(filename string) (api.KeyCreateRequest, error) {
   184  	keyContents, err := os.ReadFile(filename)
   185  
   186  	if err != nil {
   187  		return api.KeyCreateRequest{}, err
   188  	}
   189  
   190  	backupID := strings.Split(filepath.Base(filename), ".")[0]
   191  	keyInfo, err := ssh.ParsePubKey(backupID, keyContents)
   192  	if err != nil {
   193  		return api.KeyCreateRequest{}, fmt.Errorf("%s is not a valid ssh key", filename)
   194  	}
   195  	return api.KeyCreateRequest{ID: keyInfo.ID, Public: keyInfo.Public, Name: filename}, nil
   196  }