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

Extend cilium config to expose all active configurations. Add subcommand cilium config get to get configurations from CLI #16519

Merged
merged 3 commits into from
Sep 3, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions Documentation/cmdref/cilium_config.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions Documentation/cmdref/cilium_config_get.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions api/v1/models/daemon_configuration_status.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions api/v1/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2198,6 +2198,10 @@ definitions:
egress-multi-home-ip-rule-compat:
description: Configured compatibility mode for --egress-multi-home-ip-rule-compat
type: boolean
daemonConfigurationMap:
description: Config map which contains all the active daemon configurations
additionalProperties:
type: object
DatapathMode:
description: Datapath mode
type: string
Expand Down
12 changes: 12 additions & 0 deletions api/v1/server/embedded_spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

82 changes: 67 additions & 15 deletions cilium/cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ package cmd
import (
"fmt"
"os"
"reflect"
"sort"
"strconv"
"strings"

Expand All @@ -16,7 +18,10 @@ import (
"github.com/spf13/cobra"
)

var numPages int
var (
numPages int
listReadOnlyConfigurations bool
)

// configCmd represents the config command
var configCmd = &cobra.Command{
Expand All @@ -37,6 +42,7 @@ var configCmd = &cobra.Command{
func init() {
rootCmd.AddCommand(configCmd)
configCmd.Flags().BoolVarP(&listOptions, "list-options", "", false, "List available options")
configCmd.Flags().BoolVarP(&listReadOnlyConfigurations, "read-only", "r", false, "Display read only configurations")
configCmd.Flags().IntVarP(&numPages, "num-pages", "n", 0, "Number of pages for perf ring buffer. New values have to be > 0")
command.AddJSONOutput(configCmd)
}
Expand All @@ -58,20 +64,7 @@ func configDaemon(cmd *cobra.Command, opts []string) {
dOpts["MonitorNumPages"] = strconv.Itoa(numPages)
}
} else if len(opts) == 0 {
h3llix marked this conversation as resolved.
Show resolved Hide resolved
if command.OutputJSON() {
if err := command.PrintOutput(cfgStatus.Realized.Options); err != nil {
os.Exit(1)
}
return
}
dumpConfig(cfgStatus.Immutable)
dumpConfig(cfgStatus.Realized.Options)
fmt.Printf("%-24s %s\n", "k8s-configuration", cfgStatus.K8sConfiguration)
fmt.Printf("%-24s %s\n", "k8s-endpoint", cfgStatus.K8sEndpoint)
fmt.Printf("%-24s %s\n", "PolicyEnforcement", cfgStatus.Realized.PolicyEnforcement)
if cfgStatus.NodeMonitor != nil {
fmt.Printf("%-24s %d\n", "MonitorNumPages", cfgStatus.NodeMonitor.Npages)
}
printConfigurations(cfgStatus)
return
}

Expand Down Expand Up @@ -111,3 +104,62 @@ func configDaemon(cmd *cobra.Command, opts []string) {
Fatalf("Unable to change agent configuration: %s\n", err)
}
}

func printConfigurations(cfgStatus *models.DaemonConfigurationStatus) {
if command.OutputJSON() {
if listReadOnlyConfigurations {
if err := command.PrintOutput(cfgStatus.DaemonConfigurationMap); err != nil {
Fatalf("Cannot show configuratons: %v", err)
}
} else {
if err := command.PrintOutput(cfgStatus.Realized.Options); err != nil {
Fatalf("Cannot show configuratons: %v", err)
}
}
return
}
if listReadOnlyConfigurations {
dumpReadOnlyConfigs(cfgStatus)
} else {
dumpReadWriteConfigs(cfgStatus)
}
}

func dumpReadOnlyConfigs(cfgStatus *models.DaemonConfigurationStatus) {
fmt.Println("#### Read-only configurations ####")
keys := make([]string, 0, len(cfgStatus.DaemonConfigurationMap))
for k := range cfgStatus.DaemonConfigurationMap {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
v := cfgStatus.DaemonConfigurationMap[k]
if reflect.ValueOf(v).Kind() == reflect.Map {
mapString := make(map[string]string)
m, ok := v.(map[string]interface{})
if ok {
fmt.Println(k)
for key, value := range m {
mapString[key] = fmt.Sprintf("%v", value)
}
dumpConfig(mapString, true)
continue
} else {
fmt.Fprintf(os.Stderr, "Error: cannot cast daemon config map to map[string]interface{}\n")
}
}
fmt.Printf("%-34s: %v\n", k, v)
}
fmt.Printf("%-34s: %s\n", "k8s-configuration", cfgStatus.K8sConfiguration)
fmt.Printf("%-34s: %s\n", "k8s-endpoint", cfgStatus.K8sEndpoint)
dumpConfig(cfgStatus.Immutable, false)
}

func dumpReadWriteConfigs(cfgStatus *models.DaemonConfigurationStatus) {
fmt.Println("##### Read-write configurations #####")
dumpConfig(cfgStatus.Realized.Options, false)
if cfgStatus.NodeMonitor != nil {
fmt.Printf("%-34s: %d\n", "MonitorNumPages", cfgStatus.NodeMonitor.Npages)
}
fmt.Printf("%-34s: %s\n", "PolicyEnforcement", cfgStatus.Realized.PolicyEnforcement)
}
74 changes: 74 additions & 0 deletions cilium/cmd/config_get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2021 Authors of Cilium

package cmd

import (
"fmt"
"regexp"
"strings"

"github.com/cilium/cilium/pkg/command"
"github.com/cilium/cilium/test/helpers"

"github.com/spf13/cobra"
)

var (
removeHyphen = regexp.MustCompile(`[^\w]`)
)

// configGetCmd represents the config get command
var configGetCmd = &cobra.Command{
Use: "get <config name>",
Short: "Retrieve cilium configuration",
PreRun: requireConfigName,
Run: func(cmd *cobra.Command, args []string) {
// removing hyphen from the config name and transforming it to lower case
configName := removeHyphen.ReplaceAllString(strings.ToLower(args[0]), "")
resp, err := client.ConfigGet()
if err != nil {
Fatalf("Error while retrieving configuration: %s", err)
}
if resp.Status == nil {
Fatalf("Empty configuration status returned")
}

readWriteConfigMap := make(map[string]interface{})
readOnlyConfigMap := resp.Status.DaemonConfigurationMap

for k, v := range resp.Status.Realized.Options {
readWriteConfigMap[k] = v
}
readWriteConfigMap[helpers.PolicyEnforcement] = resp.Status.Realized.PolicyEnforcement

// Key values are named as field names of `DaemonConfig` struct
// to match configuration input, map keys are transformed to lower case
readWriteConfigMap = mapKeysToLowerCase(readWriteConfigMap)
readOnlyConfigMap = mapKeysToLowerCase(readOnlyConfigMap)

// conifgMap holds both read-only and read-write configurations
configMap := mergeMaps(readOnlyConfigMap, readWriteConfigMap)

if value, ok := configMap[configName]; ok {
h3llix marked this conversation as resolved.
Show resolved Hide resolved
fmt.Printf("%v\n", value)
} else {
Fatalf("Configuration does not exist")
}
},
}

func init() {
configCmd.AddCommand(configGetCmd)
command.AddJSONOutput(configGetCmd)
}

func requireConfigName(cmd *cobra.Command, args []string) {
if len(args) < 1 {
Usagef(cmd, "Missing config name argument")
}

if args[0] == "" {
Usagef(cmd, "Empty config argument")
}
}
4 changes: 2 additions & 2 deletions cilium/cmd/endpoint_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ func configEndpoint(cmd *cobra.Command, args []string) {
return
}

dumpConfig(cfg.Immutable)
dumpConfig(cfg.Realized.Options)
dumpConfig(cfg.Immutable, false)
dumpConfig(cfg.Realized.Options, false)
return
}

Expand Down
37 changes: 33 additions & 4 deletions cilium/cmd/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"os"
"path/filepath"
"reflect"
"regexp"
"sort"
"strconv"
Expand Down Expand Up @@ -358,7 +359,7 @@ func updatePolicyKey(pa *PolicyUpdateArgs, add bool) {
}

// dumpConfig pretty prints boolean options
func dumpConfig(Opts map[string]string) {
func dumpConfig(Opts map[string]string, indented bool) {
opts := []string{}
for k := range Opts {
opts = append(opts, k)
Expand All @@ -368,15 +369,32 @@ func dumpConfig(Opts map[string]string) {
for _, k := range opts {
// XXX: Reuse the format function from *option.Library
value = Opts[k]
formatStr := "%-34s: %s\n"
if indented {
formatStr = "\t%-26s: %s\n"
}
if enabled, err := option.NormalizeBool(value); err != nil {
// If it cannot be parsed as a bool, just format the value.
fmt.Printf("%-24s %s\n", k, value)
fmt.Printf(formatStr, k, value)
} else if enabled == option.OptionDisabled {
fmt.Printf("%-24s %s\n", k, "Disabled")
fmt.Printf(formatStr, k, "Disabled")
} else {
fmt.Printf("%-24s %s\n", k, "Enabled")
fmt.Printf(formatStr, k, "Enabled")
}
}
}

func mapKeysToLowerCase(s map[string]interface{}) map[string]interface{} {
m := make(map[string]interface{})
for k, v := range s {
if reflect.ValueOf(v).Kind() == reflect.Map {
for i, j := range v.(map[string]interface{}) {
m[strings.ToLower(i)] = j
}
}
m[strings.ToLower(k)] = v
}
return m
}

// getIpv6EnableStatus api returns the EnableIPv6 status
Expand Down Expand Up @@ -406,3 +424,14 @@ func getIpv6EnableStatus() bool {
// returning the EnableIPv6 default status
return defaults.EnableIPv6
}

func mergeMaps(m1, m2 map[string]interface{}) map[string]interface{} {
m3 := make(map[string]interface{})
for k, v := range m1 {
m3[k] = v
}
for k, v := range m2 {
m3[k] = v
}
return m3
}
2 changes: 1 addition & 1 deletion clustermesh-apiserver/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (c configuration) LocalClusterName() string {
return c.clusterName
}

func (c configuration) K8sServiceProxyName() string {
func (c configuration) K8sServiceProxyNameValue() string {
return c.serviceProxyName
}

Expand Down