-
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.
- Loading branch information
1 parent
d66dbb5
commit a2d81ac
Showing
6 changed files
with
303 additions
and
0 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: MPL-2.0 | ||
|
||
package apply | ||
|
||
import ( | ||
"encoding/json" | ||
"flag" | ||
"fmt" | ||
|
||
"github.com/mitchellh/cli" | ||
"google.golang.org/protobuf/encoding/protojson" | ||
|
||
"github.com/hashicorp/consul/agent/consul" | ||
"github.com/hashicorp/consul/api" | ||
"github.com/hashicorp/consul/command/flags" | ||
"github.com/hashicorp/consul/command/helpers" | ||
"github.com/hashicorp/consul/internal/resourcehcl" | ||
"github.com/hashicorp/consul/proto-public/pbresource" | ||
) | ||
|
||
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.flags.StringVar(&c.filePath, "f", "", | ||
"File path with resource definition") | ||
|
||
c.http = &flags.HTTPFlags{} | ||
flags.Merge(c.flags, c.http.ClientFlags()) | ||
c.help = flags.Usage(help, c.flags) | ||
} | ||
|
||
func makeWriteRequest(parsedResource *pbresource.Resource) (payload *api.WriteRequest, error error) { | ||
data, err := protojson.Marshal(parsedResource.Data) | ||
if err != nil { | ||
return nil, fmt.Errorf("unrecognized hcl format: %s", err) | ||
} | ||
|
||
var d map[string]string | ||
err = json.Unmarshal(data, &d) | ||
if err != nil { | ||
return nil, fmt.Errorf("unrecognized hcl format: %s", err) | ||
} | ||
delete(d, "@type") | ||
|
||
return &api.WriteRequest{ | ||
Data: d, | ||
Metadata: parsedResource.GetMetadata(), | ||
Owner: parsedResource.GetOwner(), | ||
}, nil | ||
} | ||
|
||
func (c *cmd) Run(args []string) int { | ||
if err := c.flags.Parse(args); err != nil { | ||
if err == flag.ErrHelp { | ||
return 0 | ||
} | ||
c.UI.Error(fmt.Sprintf("Failed to parse args: %v", err)) | ||
return 1 | ||
} | ||
|
||
var parsedResource *pbresource.Resource | ||
|
||
if c.filePath != "" { | ||
data, err := helpers.LoadDataSourceNoRaw(c.filePath, nil) | ||
if err != nil { | ||
c.UI.Error(fmt.Sprintf("Failed to load data: %v", err)) | ||
return 1 | ||
} | ||
|
||
parsedResource, err = parseResource(data) | ||
if err != nil { | ||
c.UI.Error(fmt.Sprintf("Your argument format is incorrect: %s", err)) | ||
return 1 | ||
} | ||
} | ||
|
||
client, err := c.http.APIClient() | ||
if err != nil { | ||
c.UI.Error(fmt.Sprintf("Error connect to Consul agent: %s", err)) | ||
return 1 | ||
} | ||
|
||
opts := &api.QueryOptions{ | ||
Namespace: parsedResource.Id.Tenancy.GetNamespace(), | ||
Partition: parsedResource.Id.Tenancy.GetPartition(), | ||
Peer: parsedResource.Id.Tenancy.GetPeerName(), | ||
Token: c.http.Token(), | ||
} | ||
|
||
gvk := &api.GVK{ | ||
Group: parsedResource.Id.Type.GetGroup(), | ||
Version: parsedResource.Id.Type.GetGroupVersion(), | ||
Kind: parsedResource.Id.Type.GetKind(), | ||
} | ||
|
||
writeRequest, err := makeWriteRequest(parsedResource) | ||
if err != nil { | ||
c.UI.Error(fmt.Sprintf("Error parsing hcl input: %v", err)) | ||
return 1 | ||
} | ||
|
||
entry, _, err := client.Resource().Apply(gvk, parsedResource.Id.GetName(), opts, writeRequest) | ||
if err != nil { | ||
c.UI.Error(fmt.Sprintf("Error writing resource %s/%s: %v", gvk, parsedResource.Id.GetName(), err)) | ||
return 1 | ||
} | ||
|
||
b, err := json.MarshalIndent(entry, "", " ") | ||
if err != nil { | ||
c.UI.Error("Failed to encode output data") | ||
return 1 | ||
} | ||
|
||
c.UI.Info(fmt.Sprintf("%s.%s.%s '%s' created.", gvk.Group, gvk.Version, gvk.Kind, parsedResource.Id.GetName())) | ||
c.UI.Info(string(b)) | ||
return 0 | ||
} | ||
|
||
func parseResource(data string) (resource *pbresource.Resource, e error) { | ||
// parse the data | ||
raw := []byte(data) | ||
resource, err := resourcehcl.Unmarshal(raw, consul.NewTypeRegistry()) | ||
if err != nil { | ||
return nil, fmt.Errorf("Failed to decode resource from input file: %v", err) | ||
} | ||
|
||
return resource, nil | ||
} | ||
|
||
func (c *cmd) Synopsis() string { | ||
return synopsis | ||
} | ||
|
||
func (c *cmd) Help() string { | ||
return flags.Usage(c.help, nil) | ||
} | ||
|
||
const synopsis = "Writes/updates resource information" | ||
const help = ` | ||
Usage: consul resource apply -f=<file-path> | ||
Writes and/or updates a resource from the definition in the hcl file provided as argument | ||
Example: | ||
$ consul resource apply -f=demo.hcl | ||
` |
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,82 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
|
||
package apply | ||
|
||
import ( | ||
"errors" | ||
"testing" | ||
|
||
"github.com/hashicorp/consul/agent" | ||
"github.com/hashicorp/consul/testrpc" | ||
"github.com/mitchellh/cli" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestResourceApplyCommand(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") | ||
|
||
cases := []struct { | ||
name string | ||
output string | ||
}{ | ||
{ | ||
name: "sample output", | ||
output: "demo.v2.Artist 'korn' created.", | ||
}, | ||
} | ||
|
||
for _, tc := range cases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
ui := cli.NewMockUi() | ||
c := New(ui) | ||
args := []string{ | ||
"-f=../testdata/demo.hcl", | ||
"-http-addr=" + a.HTTPAddr(), | ||
"-token=root", | ||
} | ||
|
||
code := c.Run(args) | ||
require.Equal(t, 0, code) | ||
require.Empty(t, ui.ErrorWriter.String()) | ||
require.Contains(t, ui.OutputWriter.String(), tc.output) | ||
}) | ||
} | ||
} | ||
|
||
func TestResourceApplyInvalidArgs(t *testing.T) { | ||
t.Parallel() | ||
|
||
type tc struct { | ||
args []string | ||
expectedCode int | ||
expectedErr error | ||
} | ||
|
||
cases := map[string]tc{ | ||
"missing file path": { | ||
args: []string{"-f"}, | ||
expectedCode: 1, | ||
expectedErr: errors.New("Failed to parse args: flag needs an argument: -f"), | ||
}, | ||
} | ||
|
||
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()) | ||
}) | ||
} | ||
} |
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,18 @@ | ||
ID { | ||
Type = gvk("demo.v2.Artist") | ||
Name = "korn" | ||
Tenancy { | ||
Namespace = "default" | ||
Partition = "default" | ||
PeerName = "local" | ||
} | ||
} | ||
|
||
Data { | ||
Name = "Korn" | ||
Genre = "GENRE_METAL" | ||
} | ||
|
||
Metadata = { | ||
"foo" = "bar" | ||
} |