diff --git a/.vscode/launch.json b/.vscode/launch.json index c8fc96d41..639f2596c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -50,4 +50,4 @@ "description": "The image of Acorn to install." }, ] -} \ No newline at end of file +} diff --git a/pkg/cli/builder/table/funcs.go b/pkg/cli/builder/table/funcs.go index 18b7bf005..0d9254e71 100644 --- a/pkg/cli/builder/table/funcs.go +++ b/pkg/cli/builder/table/funcs.go @@ -126,6 +126,13 @@ func toKObject(obj any) (kclient.Object, bool) { } func cleanFields(obj any) any { + if ol, ok := obj.(objectList); ok { + for i, o := range ol.Items { + ol.Items[i] = cleanFields(o) + } + return ol + } + ro, ok := toKObject(obj) if ok { ro.SetManagedFields(nil) @@ -147,11 +154,38 @@ func cleanFields(obj any) any { } } ro.SetAnnotations(annotations) + + // decode secret values + if sec, ok := ro.(*apiv1.Secret); ok { + return decodeSecret(sec) + } + return ro } return obj } +func decodeSecret(sec *apiv1.Secret) any { + decodedSecret := struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + Type string `json:"type,omitempty"` + Data map[string]string `json:"data,omitempty"` + Keys []string `json:"keys,omitempty"` + }{ + TypeMeta: sec.TypeMeta, + ObjectMeta: sec.ObjectMeta, + Type: sec.Type, + Data: map[string]string{}, + Keys: sec.Keys, + } + for k, v := range sec.Data { + decodedSecret.Data[k] = string(v) + } + return decodedSecret +} + func FormatYAML(data any) (string, error) { bytes, err := yaml.Marshal(cleanFields(data)) return string(bytes) + "\n", err diff --git a/pkg/cli/builder/table/writer.go b/pkg/cli/builder/table/writer.go index 7682cb0ca..8e733182b 100644 --- a/pkg/cli/builder/table/writer.go +++ b/pkg/cli/builder/table/writer.go @@ -230,6 +230,10 @@ func (t *writer) writeDataObject(objs ...any) error { return errors.Join(t.errs...) } +type objectList struct { + Items []any `json:"items"` +} + func (t *writer) flush(closing bool) error { if len(t.errs) > 0 { return errors.Join(t.errs...) @@ -262,9 +266,7 @@ func (t *writer) flush(closing bool) error { if closing { // if we are closing, then we want to print objects its in a proper list // data structure - err := t.writeDataObject(map[string]any{ - "items": objs, - }) + err := t.writeDataObject(objectList{Items: objs}) if err != nil { return err } diff --git a/pkg/cli/secret_expose.go b/pkg/cli/secret_expose.go index 202bf9164..984660b67 100644 --- a/pkg/cli/secret_expose.go +++ b/pkg/cli/secret_expose.go @@ -66,6 +66,11 @@ func (a *Reveal) Run(cmd *cobra.Command, args []string) error { Key: entry.Key, Value: string(entry.Value), }, &secret) + if a.Output != "" { + // in non-table output, we write the source object to buffer, + // so we exit here to not write the same object multiple times (one per data key) + break + } } } diff --git a/pkg/cli/secret_test.go b/pkg/cli/secret_test.go index 59e5440f0..863e12ae7 100644 --- a/pkg/cli/secret_test.go +++ b/pkg/cli/secret_test.go @@ -183,6 +183,63 @@ func TestSecret(t *testing.T) { wantErr: false, wantOut: "NAME TYPE KEY VALUE\n", }, + { + name: "acorn secret reveal secret.withdata", fields: fields{ + All: false, + Quiet: false, + Output: "", + }, + commandContext: CommandContext{ + ClientFactory: &testdata.MockClientFactory{}, + StdOut: w, + StdErr: w, + StdIn: strings.NewReader("y\n"), + }, + args: args{ + args: []string{"reveal", "secret.withdata"}, + client: &testdata.MockClient{}, + }, + wantErr: false, + wantOut: "NAME TYPE KEY VALUE\nsecret.withdata baz qux\nsecret.withdata foo bar\n", + }, + { + name: "acorn secret reveal secret.withdata -o jsoncompact", fields: fields{ + All: false, + Quiet: false, + Output: "", + }, + commandContext: CommandContext{ + ClientFactory: &testdata.MockClientFactory{}, + StdOut: w, + StdErr: w, + StdIn: strings.NewReader("y\n"), + }, + args: args{ + args: []string{"reveal", "secret.withdata", "-o=jsoncompact"}, + client: &testdata.MockClient{}, + }, + wantErr: false, + wantOut: "{\"items\":[{\"metadata\":{\"name\":\"secret.withdata\",\"creationTimestamp\":null},\"data\":{\"baz\":\"qux\",\"foo\":\"bar\"}}]}\n", + }, + { + name: "acorn secret reveal secret.withdata secret.withdata2 -o jsoncompact", fields: fields{ + All: false, + Quiet: false, + Output: "", + }, + commandContext: CommandContext{ + ClientFactory: &testdata.MockClientFactory{}, + StdOut: w, + StdErr: w, + StdIn: strings.NewReader("y\n"), + }, + args: args{ + args: []string{"reveal", "secret.withdata", "secret.withdata2", "-o=jsoncompact"}, + client: &testdata.MockClient{}, + }, + wantErr: false, + wantOut: "{\"items\":[{\"metadata\":{\"name\":\"secret.withdata\",\"creationTimestamp\":null},\"data\":{\"baz\":\"qux\",\"foo\":\"bar\"}},{\"metadata\":{\"name\":\"secret.withdata2\",\"creationTimestamp\":null},\"data\":{\"spam\":\"eggs\"}}]}\n", + }, { name: "acorn secret reveal dne", fields: fields{ All: false, diff --git a/pkg/cli/testdata/MockClient.go b/pkg/cli/testdata/MockClient.go index 46d1cb5a2..859d0868d 100644 --- a/pkg/cli/testdata/MockClient.go +++ b/pkg/cli/testdata/MockClient.go @@ -379,6 +379,26 @@ func (m *MockClient) SecretReveal(ctx context.Context, name string) (*apiv1.Secr Data: nil, Keys: nil, }, nil + case "secret.withdata": + return &apiv1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "secret.withdata", + }, + Data: map[string][]byte{ + "foo": []byte("bar"), + "baz": []byte("qux"), + }, + }, nil + case "secret.withdata2": + return &apiv1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "secret.withdata2", + }, + Data: map[string][]byte{ + "spam": []byte("eggs"), + }, + }, nil + } return nil, nil } diff --git a/pkg/cli/testdata/all/all_test_json.txt b/pkg/cli/testdata/all/all_test_json.txt index 0d175efb2..2f1d01d3e 100644 --- a/pkg/cli/testdata/all/all_test_json.txt +++ b/pkg/cli/testdata/all/all_test_json.txt @@ -63,11 +63,7 @@ VOLUMES: { "metadata": { "name": "found.vol", - "creationTimestamp": null, - "labels": { - "acorn.io/app-name": "found", - "acorn.io/volume-name": "vol" - } + "creationTimestamp": null }, "spec": {}, "status": { diff --git a/pkg/cli/testdata/all/all_test_yaml.txt b/pkg/cli/testdata/all/all_test_yaml.txt index d8e1a1d89..fd17fc5d6 100644 --- a/pkg/cli/testdata/all/all_test_yaml.txt +++ b/pkg/cli/testdata/all/all_test_yaml.txt @@ -44,9 +44,6 @@ VOLUMES: items: - metadata: creationTimestamp: null - labels: - acorn.io/app-name: found - acorn.io/volume-name: vol name: found.vol spec: {} status: diff --git a/pkg/cli/volumes_test.go b/pkg/cli/volumes_test.go index 14747e30c..b4807ea30 100644 --- a/pkg/cli/volumes_test.go +++ b/pkg/cli/volumes_test.go @@ -113,7 +113,7 @@ func TestVolume(t *testing.T) { client: &testdata.MockClient{}, }, wantErr: false, - wantOut: "{\n \"items\": [\n {\n \"metadata\": {\n \"name\": \"found.vol\",\n \"creationTimestamp\": null,\n \"labels\": {\n \"acorn.io/app-name\": \"found\",\n \"acorn.io/volume-name\": \"vol\"\n }\n },\n \"spec\": {},\n \"status\": {\n \"appName\": \"found\",\n \"appPublicName\": \"found\",\n \"volumeName\": \"vol\",\n \"columns\": {}\n }\n }\n ]\n}\n\n", + wantOut: "{\n \"items\": [\n {\n \"metadata\": {\n \"name\": \"found.vol\",\n \"creationTimestamp\": null\n },\n \"spec\": {},\n \"status\": {\n \"appName\": \"found\",\n \"appPublicName\": \"found\",\n \"volumeName\": \"vol\",\n \"columns\": {}\n }\n }\n ]\n}\n\n", }, { name: "acorn volume -o yaml", fields: fields{ @@ -126,7 +126,7 @@ func TestVolume(t *testing.T) { client: &testdata.MockClient{}, }, wantErr: false, - wantOut: "---\nitems:\n- metadata:\n creationTimestamp: null\n labels:\n acorn.io/app-name: found\n acorn.io/volume-name: vol\n name: found.vol\n spec: {}\n status:\n appName: found\n appPublicName: found\n columns: {}\n volumeName: vol\n\n", + wantOut: "---\nitems:\n- metadata:\n creationTimestamp: null\n name: found.vol\n spec: {}\n status:\n appName: found\n appPublicName: found\n columns: {}\n volumeName: vol\n\n", }, { name: "acorn volume found.vol", fields: fields{