cmd/feature.go
Rama master · réplica de lectura del repositorio original.
package cmd
import (
"bytes"
"encoding/json"
"fmt"
"net/url"
"os"
"strings"
"github.com/cozy/cozy-stack/client/request"
"github.com/cozy/cozy-stack/pkg/consts"
"github.com/spf13/cobra"
)
var flagWithSources bool
var featureCmdGroup = &cobra.Command{
Use: "features <command>",
Aliases: []string{"feature"},
Short: "Manage the feature flags",
}
var featureShowCmd = &cobra.Command{
Use: "show",
Short: `Display the computed feature flags for an instance`,
Long: `
cozy-stack feature show displays the feature flags that are shown by apps.
`,
Example: `$ cozy-stack feature show --domain cozy.localhost:8080`,
RunE: func(cmd *cobra.Command, args []string) error {
if flagDomain == "" {
errPrintfln("%s", errMissingDomain)
return cmd.Usage()
}
c := newClient(flagDomain, consts.Settings)
req := &request.Options{
Method: "GET",
Path: "/settings/flags",
}
if flagWithSources {
req.Queries = url.Values{"include": {"source"}}
}
res, err := c.Req(req)
if err != nil {
return err
}
defer res.Body.Close()
var obj struct {
Data struct {
Attributes map[string]json.RawMessage `json:"attributes"`
} `json:"data"`
Included []struct {
ID string `json:"id"`
Attributes map[string]json.RawMessage `json:"attributes"`
} `json:"included"`
}
if err = json.NewDecoder(res.Body).Decode(&obj); err != nil {
return err
}
for k, v := range obj.Data.Attributes {
fmt.Fprintf(os.Stdout, "- %s: %s\n", k, string(v))
}
if len(obj.Included) > 0 {
fmt.Fprintf(os.Stdout, "\nSources:\n")
for _, source := range obj.Included {
fmt.Fprintf(os.Stdout, "- %s\n", source.ID)
for k, v := range source.Attributes {
fmt.Fprintf(os.Stdout, "\t- %s: %s\n", k, string(v))
}
}
}
return nil
},
}
var featureFlagCmd = &cobra.Command{
Use: "flags",
Aliases: []string{"flag"},
Short: `Display and update the feature flags for an instance`,
Long: `
cozy-stack feature flags displays the feature flags that are specific to an instance.
It can also take a list of flags to update.
If you give a null value, the flag will be removed.
`,
Example: `$ cozy-stack feature flags --domain cozy.localhost:8080 '{"add_this_flag": true, "remove_this_flag": null}'`,
RunE: func(cmd *cobra.Command, args []string) error {
if flagDomain == "" {
errPrintfln("%s", errMissingDomain)
return cmd.Usage()
}
ac := newAdminClient()
req := request.Options{
Method: "GET",
Path: fmt.Sprintf("/instances/%s/feature/flags", flagDomain),
}
if len(args) > 0 {
req.Method = "PATCH"
req.Body = strings.NewReader(args[0])
}
res, err := ac.Req(&req)
if err != nil {
return err
}
defer res.Body.Close()
var obj map[string]json.RawMessage
if err = json.NewDecoder(res.Body).Decode(&obj); err != nil {
return err
}
for k, v := range obj {
fmt.Fprintf(os.Stdout, "- %s: %s\n", k, string(v))
}
return nil
},
}
var featureSetCmd = &cobra.Command{
Use: "sets",
Short: `Display and update the feature sets for an instance`,
Long: `
cozy-stack feature sets displays the feature sets coming from the manager.
It can also take a space-separated list of sets that will replace the previous
list (no merge).
All the sets can be removed by setting an empty list ('').
`,
Example: `$ cozy-stack feature sets --domain cozy.localhost:8080 'set1 set2'`,
RunE: func(cmd *cobra.Command, args []string) error {
if flagDomain == "" {
errPrintfln("%s", errMissingDomain)
return cmd.Usage()
}
ac := newAdminClient()
req := request.Options{
Method: "GET",
Path: fmt.Sprintf("/instances/%s/feature/sets", flagDomain),
}
if len(args) > 0 {
list := args
if len(args) == 1 {
list = strings.Fields(args[0])
}
buf, err := json.Marshal(list)
if err != nil {
return err
}
req.Method = "PUT"
req.Body = bytes.NewReader(buf)
}
res, err := ac.Req(&req)
if err != nil {
return err
}
defer res.Body.Close()
var sets []string
if err = json.NewDecoder(res.Body).Decode(&sets); err != nil {
return err
}
for _, set := range sets {
fmt.Fprintf(os.Stdout, "- %v\n", set)
}
return nil
},
}
var featureRatioCmd = &cobra.Command{
Use: "ratio <context-name>",
Aliases: []string{"context"},
Short: `Display and update the feature flags for a context`,
Long: `
cozy-stack feature ratio displays the feature flags for a context.
It can also create, update, or remove flags (with a ratio and value).
To remove a flag, set it to an empty array (or null).
`,
Example: `$ cozy-stack feature ratio --context beta '{"set_this_flag": [{"ratio": 0.1, "value": 1}, {"ratio": 0.9, "value": 2}] }'`,
RunE: func(cmd *cobra.Command, args []string) error {
if flagContext == "" {
return cmd.Usage()
}
ac := newAdminClient()
req := request.Options{
Method: "GET",
Path: fmt.Sprintf("/instances/feature/contexts/%s", flagContext),
}
if len(args) > 0 {
req.Method = "PATCH"
req.Body = strings.NewReader(args[0])
}
res, err := ac.Req(&req)
if err != nil {
return err
}
defer res.Body.Close()
var obj map[string]json.RawMessage
if err = json.NewDecoder(res.Body).Decode(&obj); err != nil {
return err
}
for k, v := range obj {
fmt.Fprintf(os.Stdout, "- %s: %s\n", k, string(v))
}
return nil
},
}
var featureConfigCmd = &cobra.Command{
Use: "config <context-name>",
Short: `Display the feature flags from configuration for a context`,
Long: `
cozy-stack feature config displays the feature flags from configuration for a context.
These flags are read only and can only be updated by changing configuration and restarting the stack.
`,
Example: `$ cozy-stack feature config --context beta`,
RunE: func(cmd *cobra.Command, args []string) error {
if flagContext == "" {
return cmd.Usage()
}
ac := newAdminClient()
req := request.Options{
Method: "GET",
Path: fmt.Sprintf("/instances/feature/config/%s", flagContext),
}
res, err := ac.Req(&req)
if err != nil {
return err
}
defer res.Body.Close()
var obj map[string]json.RawMessage
if err = json.NewDecoder(res.Body).Decode(&obj); err != nil {
return err
}
for k, v := range obj {
fmt.Fprintf(os.Stdout, "- %s: %s\n", k, string(v))
}
return nil
},
}
var featureDefaultCmd = &cobra.Command{
Use: "defaults",
Short: `Display and update the default values for feature flags`,
Long: `
cozy-stack feature defaults displays the default values for feature flags.
It can also take a list of flags to update.
If you give a null value, the flag will be removed.
`,
Example: `$ cozy-stack feature defaults '{"add_this_flag": true, "remove_this_flag": null}'`,
RunE: func(cmd *cobra.Command, args []string) error {
ac := newAdminClient()
req := request.Options{
Method: "GET",
Path: "/instances/feature/defaults",
}
if len(args) > 0 {
req.Method = "PATCH"
req.Body = strings.NewReader(args[0])
}
res, err := ac.Req(&req)
if err != nil {
return err
}
defer res.Body.Close()
var obj map[string]json.RawMessage
if err = json.NewDecoder(res.Body).Decode(&obj); err != nil {
return err
}
for k, v := range obj {
fmt.Fprintf(os.Stdout, "- %s: %s\n", k, string(v))
}
return nil
},
}
func init() {
featureShowCmd.Flags().StringVar(&flagDomain, "domain", "", "Specify the domain name of the instance")
featureShowCmd.Flags().BoolVar(&flagWithSources, "source", false, "Show the sources of the feature flags")
featureFlagCmd.Flags().StringVar(&flagDomain, "domain", "", "Specify the domain name of the instance")
featureSetCmd.Flags().StringVar(&flagDomain, "domain", "", "Specify the domain name of the instance")
featureRatioCmd.Flags().StringVar(&flagContext, "context", "", "The context for the feature flags")
featureConfigCmd.Flags().StringVar(&flagContext, "context", "", "The context for the feature flags")
featureCmdGroup.AddCommand(featureShowCmd)
featureCmdGroup.AddCommand(featureFlagCmd)
featureCmdGroup.AddCommand(featureSetCmd)
featureCmdGroup.AddCommand(featureRatioCmd)
featureCmdGroup.AddCommand(featureConfigCmd)
featureCmdGroup.AddCommand(featureDefaultCmd)
RootCmd.AddCommand(featureCmdGroup)
}