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

feat: API and commands for managing metadata information #153

Merged
merged 11 commits into from
Jun 10, 2024
1 change: 1 addition & 0 deletions cmd/plugin/rpaasv2/cmd/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func NewApp(o, e io.Writer, client rpaasclient.Client) (app *cli.App) {
NewCmdShell(),
NewCmdLogs(),
NewCmdExtraFiles(),
NewCmdMetadata(),
}
app.Flags = []cli.Flag{
&cli.StringFlag{
Expand Down
241 changes: 241 additions & 0 deletions cmd/plugin/rpaasv2/cmd/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
// Copyright 2024 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cmd

import (
"fmt"
"io"
"strings"

"github.com/urfave/cli/v2"

"github.com/tsuru/rpaas-operator/pkg/rpaas/client/types"
)

func NewCmdMetadata() *cli.Command {
return &cli.Command{
Name: "metadata",
Usage: "Manages metadata information of rpaasv2 instances",
Subcommands: []*cli.Command{
NewCmdGetMetadata(),
NewCmdSetMetadata(),
NewCmdUnsetMetadata(),
},
}
}

func NewCmdGetMetadata() *cli.Command {
return &cli.Command{
Name: "get",
Usage: "Shows metadata information of an instance",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "service",
Aliases: []string{"tsuru-service", "s"},
Usage: "the Tsuru service name",
},
&cli.StringFlag{
Name: "instance",
Aliases: []string{"tsuru-service-instance", "i"},
Usage: "the reverse proxy instance name",
Required: true,
},
&cli.BoolFlag{
Name: "json",
Usage: "show as JSON instead of go template format",
Value: false,
},
},
Before: setupClient,
Action: runGetMetadata,
}
}

func writeMetadata(w io.Writer, metadata *types.Metadata) {
if len(metadata.Labels) > 0 {
fmt.Fprintf(w, "Labels:\n")
for _, v := range metadata.Labels {
fmt.Fprintf(w, " %s: %s\n", v.Name, v.Value)
}
}

if len(metadata.Annotations) > 0 {
fmt.Fprintf(w, "Annotations:\n")
for _, v := range metadata.Annotations {
fmt.Fprintf(w, " %s: %s\n", v.Name, v.Value)
}
}

if len(metadata.Labels) == 0 && len(metadata.Annotations) == 0 {
fmt.Fprintf(w, "No metadata found\n")
}
}

func runGetMetadata(c *cli.Context) error {
client, err := getClient(c)
if err != nil {
return err
}

metadata, err := client.GetMetadata(c.Context, c.String("instance"))
if err != nil {
return err
}

if outputAsJSON := c.Bool("json"); outputAsJSON {
return writeJSON(c.App.Writer, metadata)
}

writeMetadata(c.App.Writer, metadata)
return nil
}

func NewCmdSetMetadata() *cli.Command {
return &cli.Command{
Name: "set",
Usage: "Sets metadata information of an instance",
ArgsUsage: "<NAME=value> [NAME=value] ...",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "service",
Aliases: []string{"tsuru-service", "s"},
Usage: "the Tsuru service name",
},
&cli.StringFlag{
Name: "instance",
Aliases: []string{"tsuru-service-instance", "i"},
Usage: "the reverse proxy instance name",
Required: true,
},
&cli.StringFlag{
Name: "type",
Aliases: []string{"t"},
Usage: "the type of metadata (label or annotation)",
Required: true,
},
},
Before: setupClient,
Action: runSetMetadata,
}
}

func isValidMetadataType(metaType string) bool {
return metaType == "label" || metaType == "annotation"
}

func createMetadata(meta []string, metaType string, isSet bool) (*types.Metadata, error) {
metadata := &types.Metadata{}

for _, kv := range meta {
var item types.MetadataItem
if isSet {
if !strings.Contains(kv, "=") {
return nil, fmt.Errorf("invalid NAME=value pair: %q", kv)
}
item.Name = strings.Split(kv, "=")[0]
item.Value = strings.Split(kv, "=")[1]
} else {
item.Name = kv
}

if metaType == "label" {
metadata.Labels = append(metadata.Labels, item)
} else {
metadata.Annotations = append(metadata.Annotations, item)
}
}

return metadata, nil
}

func runSetMetadata(c *cli.Context) error {
keyValues := c.Args().Slice()
metaType := c.String("type")

if len(keyValues) == 0 {
return fmt.Errorf("at least one NAME=value pair is required")
}

if !isValidMetadataType(metaType) {
return fmt.Errorf("invalid metadata type: %q", metaType)
}

metadata, err := createMetadata(keyValues, metaType, true)
if err != nil {
return err
}

client, err := getClient(c)
if err != nil {
return err
}

err = client.SetMetadata(c.Context, c.String("instance"), metadata)
if err != nil {
return err
}

fmt.Fprintln(c.App.Writer, "Metadata updated successfully")

return nil
}

func NewCmdUnsetMetadata() *cli.Command {
return &cli.Command{
Name: "unset",
Usage: "Unsets metadata information of an instance",
ArgsUsage: "NAME [NAME] ...",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "service",
Aliases: []string{"tsuru-service", "s"},
Usage: "the Tsuru service name",
},
&cli.StringFlag{
Name: "instance",
Aliases: []string{"tsuru-service-instance", "i"},
Usage: "the reverse proxy instance name",
Required: true,
},
&cli.StringFlag{
Name: "type",
Aliases: []string{"t"},
Usage: "the type of metadata (label or annotation)",
Required: true,
},
},
Before: setupClient,
Action: runUnsetMetadata,
}
}

func runUnsetMetadata(c *cli.Context) error {
keys := c.Args().Slice()
metaType := c.String("type")

if len(keys) == 0 {
return fmt.Errorf("at least one NAME is required")
}

if !isValidMetadataType(metaType) {
return fmt.Errorf("invalid metadata type: %q", metaType)
}

metadata, _ := createMetadata(keys, metaType, false)

client, err := getClient(c)
if err != nil {
return err
}

err = client.UnsetMetadata(c.Context, c.String("instance"), metadata)
if err != nil {
return err
}

fmt.Fprintln(c.App.Writer, "Metadata removed successfully")

return nil
}
Loading
Loading