Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add logic to display metadata by process #219

Merged
merged 2 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
github.com/pmorie/go-open-service-broker-client v0.0.0-20180330214919-dca737037ce6
github.com/sabhiram/go-gitignore v0.0.0-20171017070213-362f9845770f
github.com/tsuru/gnuflag v0.0.0-20151217162021-86b8c1b864aa
github.com/tsuru/go-tsuruclient v0.0.0-20231009130311-a01dfd615e16
github.com/tsuru/go-tsuruclient v0.0.0-20231031185823-b0abac462f59
github.com/tsuru/tablecli v0.0.0-20190131152944-7ded8a3383c6
github.com/tsuru/tsuru v0.0.0-20231009130140-65592312e508
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -733,8 +733,8 @@ github.com/tsuru/config v0.0.0-20201023175036-375aaee8b560 h1:fniQ/BmYAHdnNmY333
github.com/tsuru/config v0.0.0-20201023175036-375aaee8b560/go.mod h1:mj6t8JKWU51GScTT50XRmDj65T5XhTyNvO5FUNV5zS4=
github.com/tsuru/gnuflag v0.0.0-20151217162021-86b8c1b864aa h1:JlLQP1xa13a994p/Aau2e3K9xXYaHNoNvTDVIMHSUa4=
github.com/tsuru/gnuflag v0.0.0-20151217162021-86b8c1b864aa/go.mod h1:UibOSvkMFKRe/eiwktAPAvQG8L+p8nYsECJvu3Dgw7I=
github.com/tsuru/go-tsuruclient v0.0.0-20231009130311-a01dfd615e16 h1:gjwhjJTOuPlHhytkBXvfEzIzyYytePVvGeq7REbeBGY=
github.com/tsuru/go-tsuruclient v0.0.0-20231009130311-a01dfd615e16/go.mod h1:BmePxHey9hxrxk0kzTMHFFr7aJWXSxtlrUx6FIeV0Ic=
github.com/tsuru/go-tsuruclient v0.0.0-20231031185823-b0abac462f59 h1:RgFlupHEAJvIXH6FgtzGQqG8pS6ck3miqEXZN+a4dLs=
github.com/tsuru/go-tsuruclient v0.0.0-20231031185823-b0abac462f59/go.mod h1:BmePxHey9hxrxk0kzTMHFFr7aJWXSxtlrUx6FIeV0Ic=
github.com/tsuru/tablecli v0.0.0-20190131152944-7ded8a3383c6 h1:1XDdWFAjIbCSG1OjN9v9KdWhuM8UtYlFcfHe/Ldkchk=
github.com/tsuru/tablecli v0.0.0-20190131152944-7ded8a3383c6/go.mod h1:ztYpOhW+u1k21FEqp7nZNgpWbr0dUKok5lgGCZi+1AQ=
github.com/tsuru/tsuru v0.0.0-20231009130140-65592312e508 h1:eaMg/uBeTv6B7O+AMTq3OKqNsiA0/kB5Zkgy7ipXgcI=
Expand Down
1 change: 1 addition & 0 deletions tsuru/client/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,7 @@ type app struct {
UnitsMetrics []unitMetrics
VolumeBinds []volumeTypes.VolumeBind
ServiceInstanceBinds []tsuru.AppServiceInstanceBinds
Processes []tsuru.AppProcess
}

type appInternalAddress struct {
Expand Down
17 changes: 14 additions & 3 deletions tsuru/client/job_or_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,34 @@ import (
)

type JobOrApp struct {
Type string
val string
fs *gnuflag.FlagSet
Type string
val string
appProcess string
fs *gnuflag.FlagSet
}

func (c *JobOrApp) validate() error {
appName := c.fs.Lookup("app").Value.String()
jobName := c.fs.Lookup("job").Value.String()
var processName string

if flag := c.fs.Lookup("process"); flag != nil {
processName = flag.Value.String()
}

if appName == "" && jobName == "" {
return errors.New("job name or app name is required")
}
if appName != "" && jobName != "" {
return errors.New("please use only one of the -a/--app and -j/--job flags")
}
if processName != "" && jobName != "" {
return errors.New("please specify process just for an app")
}
if appName != "" {
c.Type = "app"
c.val = appName
c.appProcess = processName
return nil
}
c.Type = "job"
Expand Down
85 changes: 64 additions & 21 deletions tsuru/client/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"io"
"net/http"
"sort"
"strings"
Expand All @@ -26,19 +27,26 @@ Example:

var allowedTypes = []string{"label", "annotation"}

func (c *JobOrApp) getMetadata(apiClient *tsuru.APIClient) (tsuru.Metadata, error) {
func (c *JobOrApp) getMetadata(apiClient *tsuru.APIClient) (*tsuru.Metadata, map[string]tsuru.Metadata, error) {
if c.Type == "job" {
job, _, err := apiClient.JobApi.GetJob(context.Background(), c.val)
if err != nil {
return tsuru.Metadata{}, err
return nil, nil, err
}
return job.Job.Metadata, nil
return &job.Job.Metadata, nil, nil
}
app, _, err := apiClient.AppApi.AppGet(context.Background(), c.val)
if err != nil {
return tsuru.Metadata{}, err
return nil, nil, err
}
return app.Metadata, nil

processMetadata := map[string]tsuru.Metadata{}

for _, p := range app.Processes {
processMetadata[p.Name] = p.Metadata
}

return &app.Metadata, processMetadata, nil
}

func (c *JobOrApp) setMetadata(apiClient *tsuru.APIClient, metadata tsuru.Metadata, noRestart bool) (*http.Response, error) {
Expand All @@ -49,10 +57,19 @@ func (c *JobOrApp) setMetadata(apiClient *tsuru.APIClient, metadata tsuru.Metada
}
return apiClient.JobApi.UpdateJob(context.Background(), c.val, j)
}

a := tsuru.UpdateApp{
Metadata: metadata,
NoRestart: noRestart,
}

if c.appProcess == "" {
a.Metadata = metadata
} else {
a.Processes = append(a.Processes, tsuru.AppProcess{
Name: c.appProcess,
Metadata: metadata,
})
}
return apiClient.AppApi.AppUpdate(context.Background(), c.val, a)
}

Expand Down Expand Up @@ -98,7 +115,7 @@ func (c *MetadataGet) Run(context *cmd.Context, cli *cmd.Client) error {
if err != nil {
return err
}
metadata, err := joa.getMetadata(apiClient)
metadata, metadataByProcess, err := joa.getMetadata(apiClient)
if err != nil {
return err
}
Expand All @@ -107,27 +124,47 @@ func (c *MetadataGet) Run(context *cmd.Context, cli *cmd.Client) error {
return formatter.JSON(context.Stdout, metadata)
}

formatted := make([]string, 0, len(metadata.Labels))
for _, v := range metadata.Labels {
formatted = append(formatted, fmt.Sprintf("\t%s: %s", v.Name, v.Value))
if len(metadataByProcess) > 0 {
fmt.Fprintln(context.Stdout, "Metadata for app:")
}
sort.Strings(formatted)
fmt.Fprintln(context.Stdout, "Labels:")
fmt.Fprintln(context.Stdout, strings.Join(formatted, "\n"))
outputMetadata(context.Stdout, metadata)

formatted = make([]string, 0, len(metadata.Annotations))
for _, v := range metadata.Annotations {
formatted = append(formatted, fmt.Sprintf("\t%s: %s", v.Name, v.Value))
for process, processMetadata := range metadataByProcess {
if len(metadataByProcess) > 0 {
fmt.Fprintf(context.Stdout, "\nMetadata for process: %q\n", process)
}
outputMetadata(context.Stdout, &processMetadata)
}
sort.Strings(formatted)
fmt.Fprintln(context.Stdout, "Annotations:")
fmt.Fprintln(context.Stdout, strings.Join(formatted, "\n"))

return nil
}

func outputMetadata(w io.Writer, metadata *tsuru.Metadata) {
if len(metadata.Labels) > 0 {
formatted := make([]string, 0, len(metadata.Labels))
for _, v := range metadata.Labels {
formatted = append(formatted, fmt.Sprintf("\t%s: %s", v.Name, v.Value))
}
sort.Strings(formatted)
fmt.Fprintln(w, "Labels:")
fmt.Fprintln(w, strings.Join(formatted, "\n"))
}

if len(metadata.Annotations) > 0 {
formatted := make([]string, 0, len(metadata.Annotations))
for _, v := range metadata.Annotations {
formatted = append(formatted, fmt.Sprintf("\t%s: %s", v.Name, v.Value))
}
sort.Strings(formatted)
fmt.Fprintln(w, "Annotations:")
fmt.Fprintln(w, strings.Join(formatted, "\n"))
}
}

type MetadataSet struct {
cmd.AppNameMixIn
job string
processName string
fs *gnuflag.FlagSet
metadataType string
noRestart bool
Expand All @@ -136,7 +173,7 @@ type MetadataSet struct {
func (c *MetadataSet) Info() *cmd.Info {
return &cmd.Info{
Name: "metadata-set",
Usage: "metadata set <NAME=value> [NAME=value] ... <-a/--app appname | -j/--job jobname> [-t/--type type]",
Usage: "metadata set <NAME=value> [NAME=value] ... <-a/--app appname | -j/--job jobname> <-p process> [-t/--type type]",
Desc: `Sets metadata such as labels and annotations for an application or job.`,
MinArgs: 1,
}
Expand All @@ -147,6 +184,8 @@ func (c *MetadataSet) Flags() *gnuflag.FlagSet {
c.fs = c.AppNameMixIn.Flags()
c.fs.StringVar(&c.job, "job", "", "The name of the job.")
c.fs.StringVar(&c.job, "j", "", "The name of the job.")
c.fs.StringVar(&c.processName, "process", "", "The name of process of app (optional).")
c.fs.StringVar(&c.processName, "p", "", "The name of process of app (optional).")
c.fs.StringVar(&c.metadataType, "type", "", "Metadata type: annotation or label")
c.fs.StringVar(&c.metadataType, "t", "", "Metadata type: annotation or label")
c.fs.BoolVar(&c.noRestart, "no-restart", false, "Sets metadata without restarting the application")
Expand Down Expand Up @@ -219,6 +258,7 @@ func validateType(t string) error {
type MetadataUnset struct {
cmd.AppNameMixIn
job string
processName string
fs *gnuflag.FlagSet
metadataType string
noRestart bool
Expand All @@ -238,6 +278,8 @@ func (c *MetadataUnset) Flags() *gnuflag.FlagSet {
c.fs = c.AppNameMixIn.Flags()
c.fs.StringVar(&c.job, "job", "", "The name of the job.")
c.fs.StringVar(&c.job, "j", "", "The name of the job.")
c.fs.StringVar(&c.processName, "process", "", "The name of process of app (optional).")
c.fs.StringVar(&c.processName, "p", "", "The name of process of app (optional).")
c.fs.StringVar(&c.metadataType, "type", "", "Metadata type: annotation or label")
c.fs.StringVar(&c.metadataType, "t", "", "Metadata type: annotation or label")
c.fs.BoolVar(&c.noRestart, "no-restart", false, "Sets metadata without restarting the application")
Expand All @@ -262,7 +304,8 @@ func (c *MetadataUnset) Run(ctx *cmd.Context, cli *cmd.Client) error {

items := make([]tsuru.MetadataItem, len(ctx.Args))
for i := range ctx.Args {
items[i] = tsuru.MetadataItem{Name: ctx.Args[i], Delete: true}
parts := strings.SplitN(ctx.Args[i], "=", 2)
items[i] = tsuru.MetadataItem{Name: parts[0], Delete: true}
}

var metadata tsuru.Metadata
Expand Down
133 changes: 133 additions & 0 deletions tsuru/client/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,52 @@ func (s *S) TestMetadataGetJobRun(c *check.C) {
c.Assert(stdout.String(), check.Equals, result)
}

func (s *S) TestMetadataGetAppRunWithProcesses(c *check.C) {
var stdout, stderr bytes.Buffer
jsonResult := `{
"name": "somejob",
"metadata": {
"annotations": [{
"name": "my-annotation",
"value": "some long value"
}],
"labels": [{
"name": "logging.globoi.com/backup",
"value": "true"
}]
},
"processes": [
{
"name": "web",
"metadata": {
"labels": [{
"name": "logging.globoi.com/sampling",
"value": "0.1"
}]
}
}
]
}`
result := "Metadata for app:\n" +
"Labels:\n" +
"\tlogging.globoi.com/backup: true\n" +
"Annotations:\n" +
"\tmy-annotation: some long value\n\n" +
"Metadata for process: \"web\"\n" +
"Labels:\n" +
"\tlogging.globoi.com/sampling: 0.1\n"
context := cmd.Context{
Stdout: &stdout,
Stderr: &stderr,
}
client := cmd.NewClient(&http.Client{Transport: &cmdtest.Transport{Message: jsonResult, Status: http.StatusOK}}, nil, manager)
command := MetadataGet{}
command.Flags().Parse(true, []string{"-a", "someapp"})
err := command.Run(&context, client)
c.Assert(err, check.IsNil)
c.Assert(stdout.String(), check.Equals, result)
}

func (s *S) TestMetadataSetInfo(c *check.C) {
c.Assert((&MetadataSet{}).Info(), check.NotNil)
}
Expand Down Expand Up @@ -159,6 +205,49 @@ func (s *S) TestMetadataSetRunAppWithLabel(c *check.C) {
c.Assert(stdout.String(), check.Equals, expectedOut)
}

func (s *S) TestMetadataSetRunAppWithProcess(c *check.C) {
var stdout, stderr bytes.Buffer
context := cmd.Context{
Args: []string{"test.tsuru.io/label=some-value"},
Stdout: &stdout,
Stderr: &stderr,
}
expectedOut := "app \"someapp\" has been updated!\n"

trans := &cmdtest.ConditionalTransport{
Transport: cmdtest.Transport{Status: http.StatusOK},
CondFunc: func(req *http.Request) bool {
url := strings.HasSuffix(req.URL.Path, "/apps/someapp")
method := req.Method == "PUT"
data, err := io.ReadAll(req.Body)
c.Assert(err, check.IsNil)
var payload map[string]interface{}
err = json.Unmarshal(data, &payload)
c.Assert(err, check.IsNil)
c.Assert(payload, check.DeepEquals, map[string]interface{}{
"planoverride": map[string]interface{}{},
"metadata": map[string]interface{}{},
"processes": []interface{}{
map[string]interface{}{
"name": "web",
"metadata": map[string]interface{}{
"labels": []interface{}{map[string]interface{}{"name": "test.tsuru.io/label", "value": "some-value"}},
},
},
},
})
return url && method
},
}
client := cmd.NewClient(&http.Client{Transport: trans}, nil, manager)

command := MetadataSet{}
command.Flags().Parse(true, []string{"-a", "someapp", "-t", "label", "-p", "web"})
err := command.Run(&context, client)
c.Assert(err, check.IsNil)
c.Assert(stdout.String(), check.Equals, expectedOut)
}

func (s *S) TestMetadataSetRunJobWithAnnotations(c *check.C) {
var stdout, stderr bytes.Buffer
context := cmd.Context{
Expand Down Expand Up @@ -342,6 +431,50 @@ func (s *S) TestMetadataUnsetRunAppWithLabel(c *check.C) {
c.Assert(stdout.String(), check.Equals, expectedOut)
}

func (s *S) TestMetadataUnsetRunAppWithProcess(c *check.C) {
var stdout, stderr bytes.Buffer
context := cmd.Context{
Args: []string{"test.tsuru.io/label"},
Stdout: &stdout,
Stderr: &stderr,
}
expectedOut := "app \"someapp\" has been updated!\n"

trans := &cmdtest.ConditionalTransport{
Transport: cmdtest.Transport{Status: http.StatusOK},
CondFunc: func(req *http.Request) bool {
url := strings.HasSuffix(req.URL.Path, "/apps/someapp")
method := req.Method == "PUT"
data, err := io.ReadAll(req.Body)
c.Assert(err, check.IsNil)

var payload map[string]interface{}
err = json.Unmarshal(data, &payload)
c.Assert(err, check.IsNil)
c.Assert(payload, check.DeepEquals, map[string]interface{}{
"planoverride": map[string]interface{}{},
"metadata": map[string]interface{}{},
"processes": []interface{}{
map[string]interface{}{
"name": "worker",
"metadata": map[string]interface{}{
"labels": []interface{}{map[string]interface{}{"name": "test.tsuru.io/label", "delete": true}},
},
},
},
})
return url && method
},
}
client := cmd.NewClient(&http.Client{Transport: trans}, nil, manager)

command := MetadataUnset{}
command.Flags().Parse(true, []string{"-a", "someapp", "-t", "label", "-p", "worker"})
err := command.Run(&context, client)
c.Assert(err, check.IsNil)
c.Assert(stdout.String(), check.Equals, expectedOut)
}

func (s *S) TestMetadataUnsetRunJobWithAnnotations(c *check.C) {
var stdout, stderr bytes.Buffer
context := cmd.Context{
Expand Down
Loading