From 01849da0290e8c790552e4ff70f2d46adca7ce75 Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 2 Apr 2024 23:18:40 -0700 Subject: [PATCH] 42 make remove machine better (#49) - add optional argument - add confirmation (y/n) --- go.mod | 8 ++++ go.sum | 33 ++++++++++++++++ main.go | 5 ++- pkg/actions/list-machines.go | 26 +------------ pkg/actions/remove-machine.go | 48 +++++++++++------------ pkg/retrieval/machines.go | 69 ++++++++++++++++++++++++++++++++++ pkg/retrieval/machines_test.go | 61 ++++++++++++++++++++++++++++++ test/main.go | 3 -- 8 files changed, 198 insertions(+), 55 deletions(-) create mode 100644 pkg/retrieval/machines.go create mode 100644 pkg/retrieval/machines_test.go delete mode 100644 test/main.go diff --git a/go.mod b/go.mod index 987c546..84c090a 100644 --- a/go.mod +++ b/go.mod @@ -12,19 +12,27 @@ require ( require ( github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/golang/mock v1.6.0 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect github.com/lestrrat-go/httprc v1.0.5 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/option v1.0.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/segmentio/asm v1.2.0 // indirect + github.com/stretchr/testify v1.9.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/crypto v0.21.0 // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect + golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect golang.org/x/sys v0.18.0 // indirect + golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 72a47ec..d10634b 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHH github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= @@ -12,6 +13,8 @@ github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= @@ -38,17 +41,47 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY= github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 h1:LQmS1nU0twXLA96Kt7U9qtHJEbBk3z6Q0V4UXjZkpr4= +golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 h1:0c3L82FDQ5rt1bjTBlchS8t6RQ6299/+5bWMnRLh+uI= +golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 8b22691..8348b93 100644 --- a/main.go +++ b/main.go @@ -46,8 +46,9 @@ func main() { Action: actions.ListMachines, }, { - Name: "remove-machine", - Action: actions.RemoveMachine, + Name: "remove-machine", + ArgsUsage: "[machine-name]", + Action: actions.RemoveMachine, }, { Name: "reset", diff --git a/pkg/actions/list-machines.go b/pkg/actions/list-machines.go index 15ae026..895034d 100644 --- a/pkg/actions/list-machines.go +++ b/pkg/actions/list-machines.go @@ -1,12 +1,10 @@ package actions import ( - "encoding/json" "fmt" - "net/http" "os" - "github.com/therealpaulgg/ssh-sync/pkg/dto" + "github.com/therealpaulgg/ssh-sync/pkg/retrieval" "github.com/therealpaulgg/ssh-sync/pkg/utils" "github.com/urfave/cli/v2" ) @@ -24,27 +22,7 @@ func ListMachines(c *cli.Context) error { if err != nil { return err } - url := profile.ServerUrl - url.Path = "/api/v1/machines/" - req, err := http.NewRequest("GET", url.String(), nil) - if err != nil { - return err - } - token, err := utils.GetToken() - if err != nil { - return err - } - req.Header.Set("Authorization", "Bearer "+token) - req.Header.Set("Content-Type", "application/json") - resp, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("unexpected status code: %d", resp.StatusCode) - } - machines := []dto.MachineDto{} - err = json.NewDecoder(resp.Body).Decode(&machines) + machines, err := retrieval.GetMachines(profile) if err != nil { return err } diff --git a/pkg/actions/remove-machine.go b/pkg/actions/remove-machine.go index c04d78a..6d81608 100644 --- a/pkg/actions/remove-machine.go +++ b/pkg/actions/remove-machine.go @@ -2,13 +2,12 @@ package actions import ( "bufio" - "bytes" - "encoding/json" "fmt" - "net/http" "os" + "github.com/samber/lo" "github.com/therealpaulgg/ssh-sync/pkg/dto" + "github.com/therealpaulgg/ssh-sync/pkg/retrieval" "github.com/therealpaulgg/ssh-sync/pkg/utils" "github.com/urfave/cli/v2" ) @@ -26,36 +25,33 @@ func RemoveMachine(c *cli.Context) error { if err != nil { return err } - fmt.Print("Please enter the machine name: ") + answer := c.Args().First() scanner := bufio.NewScanner(os.Stdin) - var answer string - if err := utils.ReadLineFromStdin(scanner, &answer); err != nil { - return err + if answer == "" { + fmt.Print("Please enter the machine name: ") + if err := utils.ReadLineFromStdin(scanner, &answer); err != nil { + return err + } } - buf := new(bytes.Buffer) - if err := json.NewEncoder(buf).Encode(dto.MachineDto{ - Name: answer, - }); err != nil { - return err - } - url := profile.ServerUrl - url.Path = "/api/v1/machines/" - req, err := http.NewRequest("DELETE", url.String(), buf) + machines, err := retrieval.GetMachines(profile) if err != nil { return err } - token, err := utils.GetToken() - if err != nil { - return err + _, exists := lo.Find(machines, func(x dto.MachineDto) bool { + return x.Name == answer + }) + if !exists { + fmt.Println("Machine not found in your list, exiting.") + return nil } - req.Header.Set("Authorization", "Bearer "+token) - req.Header.Set("Content-Type", "application/json") - resp, err := http.DefaultClient.Do(req) - if err != nil { + fmt.Println("This will remove this machine's public key from your account and you will no longer be able to use it to perform operations on your account.") + fmt.Printf("Please confirm your intent to delete the following machine: %s (y/n): ", answer) + if err := utils.ReadLineFromStdin(scanner, &answer); err != nil { return err } - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("unexpected status code: %d", resp.StatusCode) + if answer != "y" { + return nil } - return nil + err = retrieval.DeleteMachine(profile, answer) + return err } diff --git a/pkg/retrieval/machines.go b/pkg/retrieval/machines.go new file mode 100644 index 0000000..9bf55eb --- /dev/null +++ b/pkg/retrieval/machines.go @@ -0,0 +1,69 @@ +package retrieval + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + + "github.com/therealpaulgg/ssh-sync/pkg/dto" + "github.com/therealpaulgg/ssh-sync/pkg/models" + "github.com/therealpaulgg/ssh-sync/pkg/utils" +) + +func GetMachines(profile *models.Profile) ([]dto.MachineDto, error) { + url := profile.ServerUrl + url.Path = "/api/v1/machines/" + req, err := http.NewRequest("GET", url.String(), nil) + if err != nil { + return nil, err + } + token, err := utils.GetToken() + if err != nil { + return nil, err + } + req.Header.Set("Authorization", "Bearer "+token) + req.Header.Set("Content-Type", "application/json") + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } + machines := []dto.MachineDto{} + err = json.NewDecoder(resp.Body).Decode(&machines) + if err != nil { + return nil, err + } + return machines, nil +} + +func DeleteMachine(profile *models.Profile, machineName string) error { + buf := new(bytes.Buffer) + if err := json.NewEncoder(buf).Encode(dto.MachineDto{ + Name: machineName, + }); err != nil { + return err + } + url := profile.ServerUrl + url.Path = "/api/v1/machines/" + req, err := http.NewRequest("DELETE", url.String(), buf) + if err != nil { + return err + } + token, err := utils.GetToken() + if err != nil { + return err + } + req.Header.Set("Authorization", "Bearer "+token) + req.Header.Set("Content-Type", "application/json") + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } + return nil +} diff --git a/pkg/retrieval/machines_test.go b/pkg/retrieval/machines_test.go new file mode 100644 index 0000000..13cb523 --- /dev/null +++ b/pkg/retrieval/machines_test.go @@ -0,0 +1,61 @@ +package retrieval + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/therealpaulgg/ssh-sync/pkg/dto" + "github.com/therealpaulgg/ssh-sync/pkg/models" +) + +func TestGetMachines(t *testing.T) { + // Arrange + profile := &models.Profile{} + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + json.NewEncoder(w).Encode([]dto.MachineDto{ + { + Name: "test", + }, + }) + })) + url, _ := url.Parse(server.URL) + profile.ServerUrl = *url + // Act + machines, err := GetMachines(profile) + // Assert + assert.Nil(t, err) + assert.Equal(t, 1, len(machines)) + assert.Equal(t, "test", machines[0].Name) +} + +func TestDeleteMachine(t *testing.T) { + // Arrange + profile := &models.Profile{} + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + url, _ := url.Parse(server.URL) + profile.ServerUrl = *url + // Act + err := DeleteMachine(profile, "test") + // Assert + assert.Nil(t, err) +} + +func TestDeleteMachineDoesNotExist(t *testing.T) { + // Arrange + profile := &models.Profile{} + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + })) + url, _ := url.Parse(server.URL) + profile.ServerUrl = *url + // Act + err := DeleteMachine(profile, "test") + // Assert + assert.NotNil(t, err) +} diff --git a/test/main.go b/test/main.go deleted file mode 100644 index b8780ae..0000000 --- a/test/main.go +++ /dev/null @@ -1,3 +0,0 @@ -package test - -// TODO write tests