-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
7 changed files
with
483 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
|
||
package delete | ||
|
||
import ( | ||
"errors" | ||
"flag" | ||
"fmt" | ||
|
||
"github.com/mitchellh/cli" | ||
|
||
"github.com/hashicorp/consul/api" | ||
"github.com/hashicorp/consul/command/flags" | ||
"github.com/hashicorp/consul/command/resource" | ||
) | ||
|
||
func New(ui cli.Ui) *cmd { | ||
c := &cmd{UI: ui} | ||
c.init() | ||
return c | ||
} | ||
|
||
type cmd struct { | ||
UI cli.Ui | ||
flags *flag.FlagSet | ||
http *flags.HTTPFlags | ||
help string | ||
|
||
filePath string | ||
} | ||
|
||
func (c *cmd) init() { | ||
c.flags = flag.NewFlagSet("", flag.ContinueOnError) | ||
c.http = &flags.HTTPFlags{} | ||
c.flags.StringVar(&c.filePath, "f", "", "File path with resource definition") | ||
flags.Merge(c.flags, c.http.ClientFlags()) | ||
flags.Merge(c.flags, c.http.ServerFlags()) | ||
flags.Merge(c.flags, c.http.MultiTenancyFlags()) | ||
flags.Merge(c.flags, c.http.AddPeerName()) | ||
c.help = flags.Usage(help, c.flags) | ||
} | ||
|
||
func (c *cmd) Run(args []string) int { | ||
var gvk *api.GVK | ||
var resourceName string | ||
var opts *api.QueryOptions | ||
|
||
if err := c.flags.Parse(args); err != nil { | ||
if !errors.Is(err, flag.ErrHelp) { | ||
c.UI.Error(fmt.Sprintf("Failed to parse args: %v", err)) | ||
return 1 | ||
} | ||
} | ||
|
||
if c.flags.Lookup("f").Value.String() != "" { | ||
if c.filePath != "" { | ||
parsedResource, err := resource.ParseResourceFromFile(c.filePath) | ||
if err != nil { | ||
c.UI.Error(fmt.Sprintf("Failed to decode resource from input file: %v", err)) | ||
return 1 | ||
} | ||
|
||
if parsedResource == nil { | ||
c.UI.Error("Unable to parse the file argument") | ||
return 1 | ||
} | ||
|
||
gvk = &api.GVK{ | ||
Group: parsedResource.Id.Type.GetGroup(), | ||
Version: parsedResource.Id.Type.GetGroupVersion(), | ||
Kind: parsedResource.Id.Type.GetKind(), | ||
} | ||
resourceName = parsedResource.Id.GetName() | ||
opts = &api.QueryOptions{ | ||
Namespace: parsedResource.Id.Tenancy.GetNamespace(), | ||
Partition: parsedResource.Id.Tenancy.GetPartition(), | ||
Peer: parsedResource.Id.Tenancy.GetPeerName(), | ||
Token: c.http.Token(), | ||
} | ||
} else { | ||
c.UI.Error(fmt.Sprintf("Please provide an input file with resource definition")) | ||
return 1 | ||
} | ||
} else { | ||
if len(args) < 2 { | ||
c.UI.Error("Your argument format is incorrect: Must specify two arguments: resource type and resource name") | ||
return 1 | ||
} | ||
var err error | ||
gvk, resourceName, err = resource.GetTypeAndResourceName(args) | ||
if err != nil { | ||
c.UI.Error(fmt.Sprintf("Your argument format is incorrect: %s", err)) | ||
return 1 | ||
} | ||
|
||
inputArgs := args[2:] | ||
err = resource.ParseInputParams(inputArgs, c.flags) | ||
if err != nil { | ||
c.UI.Error(fmt.Sprintf("Error parsing input arguments: %v", err)) | ||
return 1 | ||
} | ||
if c.filePath != "" { | ||
c.UI.Warn("We ignored the -f flag if you provide gvk and resource name") | ||
} | ||
opts = &api.QueryOptions{ | ||
Namespace: c.http.Namespace(), | ||
Partition: c.http.Partition(), | ||
Peer: c.http.PeerName(), | ||
Token: c.http.Token(), | ||
} | ||
} | ||
|
||
client, err := c.http.APIClient() | ||
if err != nil { | ||
c.UI.Error(fmt.Sprintf("Error connect to Consul agent: %s", err)) | ||
return 1 | ||
} | ||
|
||
if err := client.Resource().Delete(gvk, resourceName, opts); err != nil { | ||
c.UI.Error(fmt.Sprintf("Error deleting resource %s.%s.%s/%s: %v", gvk.Group, gvk.Version, gvk.Kind, resourceName, err)) | ||
return 1 | ||
} | ||
|
||
c.UI.Info(fmt.Sprintf("%s.%s.%s/%s deleted", gvk.Group, gvk.Version, gvk.Kind, resourceName)) | ||
return 0 | ||
} | ||
|
||
func (c *cmd) Synopsis() string { | ||
return synopsis | ||
} | ||
|
||
func (c *cmd) Help() string { | ||
return flags.Usage(c.help, nil) | ||
} | ||
|
||
const synopsis = "Delete resource information" | ||
const help = ` | ||
Usage: You have two options to delete the resource specified by the given | ||
type, name, partition, namespace and peer and outputs its JSON representation. | ||
consul resource delete [type] [name] -partition=<default> -namespace=<default> -peer=<local> | ||
consul resource delete -f [resource_file_path] | ||
But you could only use one of the approaches. | ||
Example: | ||
$ consul resource delete catalog.v1alpha1.Service card-processor -partition=billing -namespace=payments -peer=eu | ||
$ consul resource delete -f resource.hcl | ||
In resource.hcl, it could be: | ||
ID { | ||
Type = gvk("catalog.v1alpha1.Service") | ||
Name = "card-processor" | ||
Tenancy { | ||
Namespace = "payments" | ||
Partition = "billing" | ||
PeerName = "eu" | ||
} | ||
} | ||
` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
package delete | ||
|
||
import ( | ||
"errors" | ||
"testing" | ||
|
||
"github.com/mitchellh/cli" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/hashicorp/consul/agent" | ||
"github.com/hashicorp/consul/command/resource/apply" | ||
"github.com/hashicorp/consul/testrpc" | ||
) | ||
|
||
func TestResourceDeleteInvalidArgs(t *testing.T) { | ||
t.Parallel() | ||
|
||
type tc struct { | ||
args []string | ||
expectedCode int | ||
expectedErr error | ||
} | ||
|
||
cases := map[string]tc{ | ||
"nil args": { | ||
args: nil, | ||
expectedCode: 1, | ||
expectedErr: errors.New("Your argument format is incorrect: Must specify two arguments: resource type and resource name"), | ||
}, | ||
"empty args": { | ||
args: []string{}, | ||
expectedCode: 1, | ||
expectedErr: errors.New("Your argument format is incorrect: Must specify two arguments: resource type and resource name"), | ||
}, | ||
"missing file path": { | ||
args: []string{"-f"}, | ||
expectedCode: 1, | ||
expectedErr: errors.New("Failed to parse args: flag needs an argument: -f"), | ||
}, | ||
"file not found": { | ||
args: []string{"-f=../testdata/test.hcl"}, | ||
expectedCode: 1, | ||
expectedErr: errors.New("Failed to load data: Failed to read file: open ../testdata/test.hcl: no such file or directory"), | ||
}, | ||
"provide type and name": { | ||
args: []string{"a.b.c"}, | ||
expectedCode: 1, | ||
expectedErr: errors.New("Your argument format is incorrect: Must specify two arguments: resource type and resource name"), | ||
}, | ||
"provide type and name with -f": { | ||
args: []string{"a.b.c", "name", "-f", "test.hcl"}, | ||
expectedCode: 1, | ||
expectedErr: errors.New("We ignored the -f flag if you provide gvk and resource name"), | ||
}, | ||
"provide type and name with -f and other flags": { | ||
args: []string{"a.b.c", "name", "-f", "test.hcl", "-namespace", "default"}, | ||
expectedCode: 1, | ||
expectedErr: errors.New("We ignored the -f flag if you provide gvk and resource name"), | ||
}, | ||
"does not provide resource name after type": { | ||
args: []string{"a.b.c", "-namespace", "default"}, | ||
expectedCode: 1, | ||
expectedErr: errors.New("Your argument format is incorrect: Must provide resource name right after type"), | ||
}, | ||
"invalid resource type format": { | ||
args: []string{"a.", "name", "-namespace", "default"}, | ||
expectedCode: 1, | ||
expectedErr: errors.New("Your argument format is incorrect: Must include resource type argument in group.verion.kind format"), | ||
}, | ||
} | ||
|
||
for desc, tc := range cases { | ||
t.Run(desc, func(t *testing.T) { | ||
ui := cli.NewMockUi() | ||
c := New(ui) | ||
|
||
code := c.Run(tc.args) | ||
|
||
require.Equal(t, tc.expectedCode, code) | ||
require.Contains(t, ui.ErrorWriter.String(), tc.expectedErr.Error()) | ||
}) | ||
} | ||
} | ||
|
||
func createResource(t *testing.T, a *agent.TestAgent) { | ||
applyUi := cli.NewMockUi() | ||
applyCmd := apply.New(applyUi) | ||
|
||
args := []string{ | ||
"-http-addr=" + a.HTTPAddr(), | ||
"-token=root", | ||
} | ||
|
||
args = append(args, []string{"-f=../testdata/demo.hcl"}...) | ||
|
||
code := applyCmd.Run(args) | ||
require.Equal(t, 0, code) | ||
require.Empty(t, applyUi.ErrorWriter.String()) | ||
} | ||
|
||
func TestResourceDelete(t *testing.T) { | ||
if testing.Short() { | ||
t.Skip("too slow for testing.Short") | ||
} | ||
|
||
t.Parallel() | ||
|
||
a := agent.NewTestAgent(t, ``) | ||
defer a.Shutdown() | ||
testrpc.WaitForTestAgent(t, a.RPC, "dc1") | ||
|
||
defaultCmdArgs := []string{ | ||
"-http-addr=" + a.HTTPAddr(), | ||
"-token=root", | ||
} | ||
cases := []struct { | ||
name string | ||
args []string | ||
expectedCode int | ||
createResource bool | ||
}{ | ||
{ | ||
name: "delete resource in hcl format", | ||
args: []string{"-f=../testdata/demo.hcl"}, | ||
expectedCode: 0, | ||
createResource: true, | ||
}, | ||
{ | ||
name: "delete resource in command line format", | ||
args: []string{"demo.v2.Artist", "korn", "-partition=default", "-namespace=default", "-peer=local"}, | ||
expectedCode: 0, | ||
createResource: true, | ||
}, | ||
{ | ||
name: "delete resource that doesn't exist in command line format", | ||
args: []string{"demo.v2.Artist", "korn", "-partition=default", "-namespace=default", "-peer=local"}, | ||
expectedCode: 0, | ||
createResource: false, | ||
}, | ||
} | ||
|
||
for _, tc := range cases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
ui := cli.NewMockUi() | ||
c := New(ui) | ||
cliArgs := append(tc.args, defaultCmdArgs...) | ||
if tc.createResource { | ||
createResource(t, a) | ||
} | ||
code := c.Run(cliArgs) | ||
require.Empty(t, ui.ErrorWriter.String()) | ||
require.Equal(t, tc.expectedCode, code) | ||
require.Contains(t, ui.OutputWriter.String(), "deleted") | ||
}) | ||
} | ||
} |
Oops, something went wrong.